__init__和__call__之间有什么区别?

774

我想了解__init____call__方法之间的区别。

比如:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

4
这实际上是两个问题:“__init__方法是什么 / 它是做什么的?”和“__call__方法是什么 / 它是做什么的?”我没有先验地认为它们有任何关联,实际上它们没有关联 - 因此没有有意义的描述它们之间的“区别”。 - Karl Knechtel
1
foo和bar有什么区别? - David Heffernan
17个回答

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)

运行:

Example(1,2,3)(7,8,9)

结果:

- init 1 2 3
- TypeError: 'Example' object is not callable

案例二:

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)

运行:

Example(1,2,3)(7,8,9)

结果:

- init 1 2 3
- call 7 8 9

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

注意:在结果输出方面,场景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;
        }
    }
}

4
与Java的比较完全超出了问题的范围。在你的例子中,你看不到任何区别是因为选择不当,数字都是一样的。 - Aquiles Carattino

4

__init__是Python类中的一个特殊方法,它是一个类的构造方法。每当一个类的对象被构造时,它就会被调用或者我们可以说它初始化了一个新对象。 例如:

    In [4]: class A:
   ...:     def __init__(self, a):
   ...:         print(a)
   ...:
   ...: a = A(10) # An argument is necessary
10

如果我们使用A(),它将会出现错误TypeError: __init__() missing 1 required positional argument: 'a',因为它需要一个参数a,这是因为有__init__
........
__call__在类中被实现时,可以帮助我们将类实例作为函数调用。
例如:
In [6]: class B:
   ...:     def __call__(self,b):
   ...:         print(b)
   ...:
   ...: b = B() # Note we didn't pass any arguments here
   ...: b(20)   # Argument passed when the object is called
   ...:
20

如果我们使用 B(),它会正常运行,因为这里没有__init__函数。


将一个对象传递给一个初始化的类对象。所以这是一个可调用的对象吗? - Ciasto piekarz

3

__init__() 方法可以:

  • 初始化类的实例。
  • 被多次调用。
  • 仅返回None

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

例如,Person 类具有以下 __init__()__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

现在,我们创建并初始化Person类的实例,如下所示:
    # Here
obj = Person("John", "Smith")

然后,__init__() 如下所示被调用:
"__init__()" is called.

接下来,我们以以下两种方式调用__call__()

obj = Person("John", "Smith")
print(obj("Hello, ")) # Here
print(obj.__call__("Hello, ")) # Here

然后,__call__() 就会被调用,如下所示:
"__init__()" is called.
Hello, John Smith # Here
Hello, John Smith # Here

而且,__init__() 可以像下面展示的那样被多次调用:

obj = Person("John", "Smith")
print(obj.__init__("Tom", "Brown")) # Here
print(obj("Hello, "))
print(obj.__call__("Hello, "))

接着,会调用__init__()方法重新初始化Person类的实例,并且__init__()方法会返回None,如下所示:

"__init__()" is called.
"__init__()" is called. # Here
None # Here
Hello, Tom Brown
Hello, Tom Brown

如果__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

以下错误发生:
TypeError: __init__() 应该返回 None,而不是 'str'
如果在 Person 类中未定义 __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

接下来,我们按照下面的方式调用obj("Hello, "):

obj = Person("John", "Smith")
obj("Hello, ") # Here

以下错误发生:
TypeError: 'Person' 对象不可调用
然后,我们像下面展示的那样调用 obj.__call__("Hello, "):
obj = Person("John", "Smith")
obj.__call__("Hello, ") # Here

以下错误发生:
AttributeError:'Person'对象没有属性“__call__”

2

我希望为您提供一些快捷方式、语法糖以及一些可用的技术,但我还没有在当前答案中看到它们。

实例化类并立即调用

在许多情况下,例如需要进行API请求时,逻辑被封装在一个类中,我们真正需要的只是将数据提供给该类并立即运行它作为一个独立实体,此时可能不需要实例化类。这就是

instance = MyClass() # instanciation
instance() # run the instance.__call__()
# now instance is not needed 

我们可以做类似于这样的事情。
class HTTPApi:

    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2

    def __call__(self, *args, **kwargs):
        return self.run(args, kwargs)

    def run(self, *args, **kwargs):
        print("hello", self.val1, self.val2, args, kwargs)
        
if __name__ == '__main__':
    # Create a class, and call it
    (HTTPApi("Value1", "Value2"))("world", 12, 213, 324, k1="one", k2="two")

调用另一个已存在的方法

我们可以将一个方法声明为__call__,而不必创建实际的__call__方法。

class MyClass:

    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2

    def run(self, *args, **kwargs):
        print("hello", self.val1, self.val2, args, kwargs)

    __call__ = run

if __name__ == '__main__':
    (MyClass("Value1", "Value"))("world", 12, 213, 324, k1="one", k2="two")


这允许声明另一个全局函数,而不是方法,出于任何原因(可能有一些原因,例如您无法修改该方法,但需要类调用它)。
def run(self, *args, **kwargs):
    print("hello",self.val1, self.val2,  args, kwargs)

class MyClass:

    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2

    __call__ = run

if __name__ == '__main__':
    (MyClass("Value1", "Value2"))("world", 12, 213, 324, k1="one", k2="two")

2
我们可以使用call方法将其他类方法用作静态方法。
class _Callable:
    def __init__(self, anycallable):
        self.__call__ = anycallable

class Model:

    def get_instance(conn, table_name):

        """ do something"""

    get_instance = _Callable(get_instance)

provs_fac = Model.get_instance(connection, "users")  

1

call 方法用于使对象表现得像函数。

>>> 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

<*There is no __call__ method so it doesn't act like function and throws error.*>

>>> 
>>> class B:
...     def __init__(self):
...         print "From init ... "
...     def __call__(self):
...         print "From call it is a function ... "
... 
>>> b = B()
From init ... 
>>> b()
From call it is a function... 
>>> 

<* __call__ method made object "b" to act like function *>

我们还可以将它传递给类变量。
class B:
    a = A()
    def __init__(self):
       print "From init ... "

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接