__init__和__call__有什么区别?

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

例如:

class test:


def __init__(self):
self.a = 10


def __call__(self):
b = 20
336046 次浏览

__init__将被视为构造函数,其中__call__方法可以用对象调用任意次数。__init____call__函数都采用默认参数。

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

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


x = Foo(1, 2, 3) # __init__

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

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


x = Foo()
x(1, 2, 3) # __call__

定义自定义__call__()方法允许将类的实例作为函数调用,而不总是修改实例本身。

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


In [2]: a = A()
init


In [3]: a()
call

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

类的实例(又名对象),可以将它们视为函数:将它们传递给其他方法/函数并调用它们。为了实现这一点,__call__类函数必须专门化。

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

在Python中,__init__()被正确定义为Class Constructor(以及__del__()是Class Destructor)。因此,__init__()__call__()之间有一个净区别:第一个构建Class up的实例,第二个使可调用作为函数的实例不影响对象本身的生命周期(即__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

__call__使类的实例可调用。 为什么要这样做?

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

但是在很多情况下,你可能想要重新定义你的对象,比如你已经完成了你的对象,并且可能需要一个新对象。使用__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
>>>
>>> class B:
...     def __init__(self):
...         print "From init ... "
...     def __call__(self):
...         print "From call ... "
...
>>> b = B()
From init ...
>>> b()
From 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")

我将尝试用一个例子来解释这一点,假设你想从斐波那契数列中打印一个固定数量的项。记住,斐波那契数列的前2项是1。例如:1、1、2、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)

输出是:

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__

如果你观察到输出__init__只被调用了一次,那就是类第一次实例化的时候,后来在没有重新初始化的情况下调用了对象。

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

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

这个例子取自Python 3模式、配方和习语

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

产出

在此处输入图片描述

上面已经给出了简短而甜蜜的答案。我想提供一些与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中,我们再次创建了另一个新实例instance 1。在场景2中, 我们只需修改已经创建的instance 1__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;
}
}
}

__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'因为它需要1个参数a因为__init__

……

__call__在Class中实现时帮助我们将Class实例作为函数调用调用。

示例:

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__函数。

因此,当您创建任何类的实例并初始化实例变量时,会调用__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")

当您像任何其他函数一样调用对象时,会调用__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

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

案例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)

运行:

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

结果:

- init 1 2 3
- call 7 8 9

打电话方法用于使对象充当函数。

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

我想提出一些捷径和语法糖,以及一些可以使用的技术,但我没有在当前的答案中看到它们。

实例化类并立即调用它

在许多情况下,例如当需要发出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")

__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__()应该返回无,而不是'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

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

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

发生以下错误:

TypeError:'Person'对象不可调用

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

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

发生以下错误:

属性错误:'Person'对象没有属性'__call__'