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)
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)
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)
小智 23
__call__
使类的实例可调用.为什么需要它?
技术上在创建对象时__init__
调用一次__new__
,以便可以初始化它.
但是在很多场景中你可能想要重新定义你的对象,比如你完成了对象,并且可能需要一个新的对象.有了__call__
你可以重新定义相同的对象,就好像它是新的.
这只是一种情况,还有更多.
Vik*_*ram 15
__init__
将被视为构造函数,其中__call__
可以使用对象多次调用方法.两者__init__
和__call__
函数都采用默认参数.
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__
仅在第一次实例化类时被调用,则稍后在没有重新初始化的情况下调用该对象.
__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)
因此,__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)
您还可以使用__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)
输出:
情况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)
上面已经提供了简短而甜蜜的答案。我想提供一些与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)
小智 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__”