__init__和__call__有什么区别?

sam*_*sam 457 python oop class object callable-object

我想知道__init____call__方法之间的区别.

例如:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20
Run Code Online (Sandbox Code Playgroud)

Cat*_*lus 633

第一个用于初始化新创建的对象,并接收用于执行此操作的参数:

class Foo:
    def __init__(self, a, b, c):
        # ...

x = Foo(1, 2, 3) # __init__
Run Code Online (Sandbox Code Playgroud)

第二个实现函数调用操作符.

class Foo:
    def __call__(self, a, b, c):
        # ...

x = Foo()
x(1, 2, 3) # __call__
Run Code Online (Sandbox Code Playgroud)

  • 因此,当调用*class*来初始化实例时使用`__init__`方法,而在调用*instance*时调用`__call__`方法 (351认同)
  • `__call__` 的实际用法是什么? (17认同)
  • 下面 Dmitriy Sintsov 的回答提出了一个非常重要的观点,所以我觉得我应该在这里引起注意:`__call__`可以返回一个*任意值*,而`__init__`*必须返回 None*。 (13认同)
  • 在实例化类时调用__init__:myfoo = Foo(1,4,7.8)__ call__是一个模板来调用已经实例化的类做一些事情让我们说类Foo:\ def __call __(self,zzz)然后,myfoo(12)调用该类来执行该类所做的事情. (5认同)
  • 这似乎是正确的,显然可以在实例的生命周期中修改实例变量,这在某些情况下可能是有益的。 (2认同)

ava*_*sal 232

__call__()在元类中定义自定义方法允许将类的实例作为函数调用,而不是始终修改实例本身.

In [1]: class A:
   ...:     def __init__(self):
   ...:         print "init"
   ...:         
   ...:     def __call__(self):
   ...:         print "call"
   ...:         
   ...:         

In [2]: a = A()
init

In [3]: a()
call
Run Code Online (Sandbox Code Playgroud)

  • `__call__` 不仅允许实例用作函数......它定义了当实例用作函数时执行的函数体。 (3认同)

Pao*_*sca 71

在Python中,函数是第一类对象,这意味着:函数引用可以在输入中传递给其他函数和/或方法,并从它们内部执行.

类的实例(也称为对象)可以被视为它们是函数:将它们传递给其他方法/函数并调用它们.为了实现这一点,__call__类功能必须是专用的.

def __call__(self, [args ...]) 它将可变数量的参数作为输入.假设x是Class的一个实例X,x.__call__(1, 2)类似于调用x(1,2)实例本身作为一个函数.

在Python中,__init__()被正确定义为类构造函数(以及__del__()类析构函数).因此,存在之间的净区别__init__()__call__():第一建立类的实例时,该第二使得这样的实例可调用一个函数将不会影响物体本身的生命周期(即__call__不影响施工/销毁生命周期),但它可以修改其内部状态(如下所示).

例.

class Stuff(object):

    def __init__(self, x, y, range):
        super(Stuff, self).__init__()
        self.x = x
        self.y = y
        self.range = range

    def __call__(self, x, y):
        self.x = x
        self.y = y
        print '__call__ with (%d,%d)' % (self.x, self.y)

    def __del__(self):
        del self.x
        del self.y
        del self.range

>>> s = Stuff(1, 2, 3)
>>> s.x
1
>>> s(7, 8)
__call__ with (7,8)
>>> s.x
7
Run Code Online (Sandbox Code Playgroud)

  • 我理解这个概念,但不理解*修改其内部状态*的特殊功能。如果我们在上面的代码中简单地用“def update”替换“def __call__”,我们就为该类提供了一个执行相同操作的“update”方法。如果下面调用“s.update(7, 8)”,它现在还可以修改内部状态。那么,“__call__”只是语法糖吗? (6认同)
  • 是的,差不多。它只是调用对象方法的快捷方式,而无需指定它。除此之外,它就像任何其他实例方法一样。有趣的是,如果您用 @classmethod 修饰它,它既可以用作类方法,又可以使实例可调用。但是由于类方法不能接受 self,所以没有状态可以传递,并且尝试将类作为方法调用会调用“__init__”,所以幸运的是它不会破坏类构造。 (3认同)

小智 23

__call__使类的实例可调用.为什么需要它?

技术上在创建对象时__init__调用一次__new__,以便可以初始化它.

但是在很多场景中你可能想要重新定义你的对象,比如你完成了对象,并且可能需要一个新的对象.有了__call__你可以重新定义相同的对象,就好像它是新的.

这只是一种情况,还有更多.

  • 对于这种特定情况,我们是否应该只创建一个新实例?这在某种程度上是有效的,可以修改和使用同一个实例. (6认同)

Vik*_*ram 15

__init__将被视为构造函数,其中__call__可以使用对象多次调用方法.两者__init____call__函数都采用默认参数.

  • `__init__` 不是构造函数,但 `__new__` 是。`__init__` 在 `__new__` 之后被调用 (3认同)

Job*_*bin 15

>>> class A:
...     def __init__(self):
...         print "From init ... "
... 
>>> a = A()
From init ... 
>>> a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no __call__ method
>>> 
>>> class B:
...     def __init__(self):
...         print "From init ... "
...     def __call__(self):
...         print "From call ... "
... 
>>> b = B()
From init ... 
>>> b()
From call ... 
>>> 
Run Code Online (Sandbox Code Playgroud)


Rut*_*ila 10

我将尝试用一个例子来解释这个,假设你想从斐波那契系列中打印一定数量的术语.请记住,斐波纳契系列的前两个项是1.例如:1,1,3,3,5,8,13 ....

您希望包含斐波那契数字的列表仅初始化一次,之后应该更新.现在我们可以使用这个__call__功能了.阅读@mudit verma的回答.这就像你希望对象可以作为函数调用,但是在你调用它时不会重新初始化.

例如:

class Recorder:
    def __init__(self):
        self._weights = []
        for i in range(0, 2):
            self._weights.append(1)
        print self._weights[-1]
        print self._weights[-2]
        print "no. above is from __init__"

    def __call__(self, t):
        self._weights = [self._weights[-1], self._weights[-1] + self._weights[-2]]
        print self._weights[-1]
        print "no. above is from __call__"

weight_recorder = Recorder()
for i in range(0, 10):
    weight_recorder(i)
Run Code Online (Sandbox Code Playgroud)

输出是:

1
1
no. above is from __init__
2
no. above is from __call__
3
no. above is from __call__
5
no. above is from __call__
8
no. above is from __call__
13
no. above is from __call__
21
no. above is from __call__
34
no. above is from __call__
55
no. above is from __call__
89
no. above is from __call__
144
no. above is from __call__
Run Code Online (Sandbox Code Playgroud)

如果您观察到输出__init__仅在第一次实例化类时被调用,则稍后在没有重新初始化的情况下调用该对象.


Dmi*_*sov 9

__call__允许返回任意值,而__init__作为构造函数隐式返回类的实例。正如其他答案正确指出的那样,__init__只调用一次,但可以__call__多次调用,以防初始化的实例分配给中间变量。

>>> class Test:
...     def __init__(self):
...         return 'Hello'
... 
>>> Test()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: __init__() should return None, not 'str'
>>> class Test2:
...     def __call__(self):
...         return 'Hello'
... 
>>> Test2()()
'Hello'
>>> 
>>> Test2()()
'Hello'
>>> 
Run Code Online (Sandbox Code Playgroud)


Aye*_*hik 8

因此,__init__在您创建任何类的实例并初始化实例变量时调用。

例子:

class User:

    def __init__(self,first_n,last_n,age):
        self.first_n = first_n
        self.last_n = last_n
        self.age = age

user1 = User("Jhone","Wrick","40")
Run Code Online (Sandbox Code Playgroud)

__call__当你调用像任何其他函数的对象被调用。

例子:

class USER:
    def __call__(self,arg):
        "todo here"
         print(f"I am in __call__ with arg : {arg} ")


user1=USER()
user1("One") #calling the object user1 and that's gonna call __call__ dunder functions
Run Code Online (Sandbox Code Playgroud)


Teo*_*ahi 7

您还可以使用__call__方法来实现装饰器

本示例摘自Python 3 Patterns,Recipes和Idioms

class decorator_without_arguments(object):
    def __init__(self, f):
        """
        If there are no decorator arguments, the function
        to be decorated is passed to the constructor.
        """
        print("Inside __init__()")
        self.f = f

    def __call__(self, *args):
        """
        The __call__ method is not called until the
        decorated function is called.
        """
        print("Inside __call__()")
        self.f(*args)
        print("After self.f( * args)")


@decorator_without_arguments
def sayHello(a1, a2, a3, a4):
    print('sayHello arguments:', a1, a2, a3, a4)


print("After decoration")
print("Preparing to call sayHello()")
sayHello("say", "hello", "argument", "list")
print("After first sayHello() call")
sayHello("a", "different", "set of", "arguments")
print("After second sayHello() call")
Run Code Online (Sandbox Code Playgroud)

输出

在此处输入图片说明

  • 您能否将输出复制为文本? (4认同)

Hoa*_*ell 6

情况1:

class Example:
    def __init__(self, a, b, c):
        self.a=a
        self.b=b
        self.c=c
        print("init", self.a, self.b, self.c)
Run Code Online (Sandbox Code Playgroud)

跑步:

Example(1,2,3)(7,8,9)
Run Code Online (Sandbox Code Playgroud)

结果:

- init 1 2 3
- TypeError: 'Example' object is not callable
Run Code Online (Sandbox Code Playgroud)

案例2:

class Example:
    def __init__(self, a, b, c):
        self.a=a
        self.b=b
        self.c=c
        print("init", self.a, self.b, self.c)
    def __call__(self, x, y, z):
        self.x=x
        self.y=y
        self.z=z
        print("call", self.x, self.y, self.z)
Run Code Online (Sandbox Code Playgroud)

跑步:

Example(1,2,3)(7,8,9)
Run Code Online (Sandbox Code Playgroud)

结果:

- init 1 2 3
- call 7 8 9
Run Code Online (Sandbox Code Playgroud)


Udd*_*tam 5

上面已经提供了简短而甜蜜的答案。我想提供一些与Java相比的实际实现。

 class test(object):
        def __init__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c
        def __call__(self, a, b, c):
            self.a = a
            self.b = b
            self.c = c


    instance1 = test(1, 2, 3)
    print(instance1.a) #prints 1

    #scenario 1
    #creating new instance instance1
    #instance1 = test(13, 3, 4)
    #print(instance1.a) #prints 13


    #scenario 2
    #modifying the already created instance **instance1**
    instance1(13,3,4)
    print(instance1.a)#prints 13
Run Code Online (Sandbox Code Playgroud)

注意:场景 1 和场景 2 在结果输出方面似乎相同。但在场景1中,我们再次创建另一个新实例instance1。在场景2中,我们只需修改已经创建的instance1__call__在这里是有益的,因为系统不需要创建新实例。

Java 中的等效项

public class Test {

    public static void main(String[] args) {
        Test.TestInnerClass testInnerClass = new Test(). new TestInnerClass(1, 2, 3);
        System.out.println(testInnerClass.a);

        //creating new instance **testInnerClass**
        testInnerClass = new Test().new TestInnerClass(13, 3, 4);
        System.out.println(testInnerClass.a);

        //modifying already created instance **testInnerClass**
        testInnerClass.a = 5;
        testInnerClass.b = 14;
        testInnerClass.c = 23;

        //in python, above three lines is done by testInnerClass(5, 14, 23). For this, we must define __call__ method

    }

    class TestInnerClass /* non-static inner class */{

        private int a, b,c;

        TestInnerClass(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 与Java进行比较完全超出了问题的范围。在您的示例中,您看不到任何差异,因为它选择不当,数字是相同的。 (4认同)

小智 5

__init__()能:

  • 初始化类的实例。
  • 被叫很多次。
  • 只能返回None

__call__()可以像实例方法一样自由使用。

例如,Person类有__init__()and ,__call__()如下所示:

class Person:
    def __init__(self, f_name, l_name):
        self.f_name = f_name
        self.l_name = l_name
        print('"__init__()" is called.')
        
    def __call__(self, arg):
        return arg + self.f_name + " " + self.l_name
Run Code Online (Sandbox Code Playgroud)

现在,我们创建并初始化Person类的实例,如下所示:

    # Here
obj = Person("John", "Smith")
Run Code Online (Sandbox Code Playgroud)

然后,__init__()调用如下所示:

"__init__()" is called.
Run Code Online (Sandbox Code Playgroud)

接下来我们__call__()通过如下2种方式调用:

obj = Person("John", "Smith")
print(obj("Hello, ")) # Here
print(obj.__call__("Hello, ")) # Here
Run Code Online (Sandbox Code Playgroud)

然后,__call__()调用如下所示:

"__init__()" is called.
Hello, John Smith # Here
Hello, John Smith # Here
Run Code Online (Sandbox Code Playgroud)

并且,__init__()可以多次调用,如下所示:

obj = Person("John", "Smith")
print(obj.__init__("Tom", "Brown")) # Here
print(obj("Hello, "))
print(obj.__call__("Hello, "))
Run Code Online (Sandbox Code Playgroud)

然后,__init__()被调用,Person类的实例被重新初始化并None返回,__init__()如下所示:

"__init__()" is called.
"__init__()" is called. # Here
None # Here
Hello, Tom Brown
Hello, Tom Brown
Run Code Online (Sandbox Code Playgroud)

并且,如果__init__()没有返回None,我们调用__init__()如下所示:

class Person:
    def __init__(self, f_name, l_name):
        self.f_name = f_name
        self.l_name = l_name
        print('"__init__()" is called.')
        return "Hello" # Here
        
    # ...

obj = Person("John", "Smith") # Here
Run Code Online (Sandbox Code Playgroud)

出现以下错误:

类型错误: __init__() 应该返回 None,而不是 'str'

并且,如果类__call__中没有定义Person

class Person:
    def __init__(self, f_name, l_name):
        self.f_name = f_name
        self.l_name = l_name
        print('"__init__()" is called.')
        
    # def __call__(self, arg):
    #     return arg + self.f_name + " " + self.l_name
Run Code Online (Sandbox Code Playgroud)

然后,我们调用obj("Hello, ")如下所示:

obj = Person("John", "Smith")
obj("Hello, ") # Here
Run Code Online (Sandbox Code Playgroud)

出现以下错误:

类型错误:“Person”对象不可调用

然后我们再次调用,obj.__call__("Hello, ")如下所示:

obj = Person("John", "Smith")
obj.__call__("Hello, ") # Here
Run Code Online (Sandbox Code Playgroud)

出现以下错误:

AttributeError:“Person”对象没有属性“__call__”