Відповіді:
Перший використовується для ініціалізації новоствореного об'єкта і отримує аргументи, які використовуються для цього:
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__
?
Визначення користувацького __call__()
методу в мета-класі дозволяє викликати екземпляр класу як функції, не завжди змінюючи сам екземпляр.
In [1]: class A:
...: def __init__(self):
...: print "init"
...:
...: def __call__(self):
...: print "call"
...:
...:
In [2]: a = A()
init
In [3]: a()
call
__call__
не тільки дозволяє екземпляр використовувати як функцію ... він визначає тіло функції, яке виконується, коли екземпляр використовується як функція.
У Python функції є першокласними об'єктами, це означає: посилання на функції можуть передаватися у введеннях до інших функцій та / або методів та виконуватись всередині них.
Екземпляри класів (aka Objects) можна трактувати так, ніби вони були функціями: передайте їх іншим методам / функціям та викликайте їх. Для досягнення цього __call__
функції класу необхідно спеціалізувати.
def __call__(self, [args ...])
Він приймає в якості змінної кількість аргументів. Якщо припустити, x
що це екземпляр класу X
, x.__call__(1, 2)
є аналогом виклику x(1,2)
або самого екземпляра як функції .
У Python __init__()
правильно визначений як Class Constructor (як і __del__()
клас Destructor). Таким чином, існує мережа відмінності між __init__()
і __call__()
: першим створює екземпляр класу вгору, другий робить такий випадок Callable як функція буде не впливаючи на життєвому цикл самого об'єкта (тобто __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
def __call__
просто на def update
, ми надаємо класу update
метод, який робить те саме. Тепер він також може змінювати внутрішній стан, якщо його називають нижче як s.update(7, 8)
. Отже, чи __call__
просто синтактикс цукор?
__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 ...
>>>
__init__
трактується як конструктор, де як __call__
методи можна викликати об'єкти будь-яку кількість разів. І функції, __init__
і __call__
функції приймають аргументи за замовчуванням.
__init__
не є конструкторською функцією, але __new__
є. __init__
називається одразу після__new__
__new__
створює екземпляр класу і отримує клас як аргумент, тоді __init__
як конструктор екземплярів, тому він отримує self
. Найпростішим способом побачити це в виклику a = Foo(1,2,3)
буде функція, яка отримає аргументи конструктора __init__
.
Я спробую пояснити це на прикладі, припустимо, ви хотіли надрукувати фіксовану кількість термінів із серій фіналів. Пам’ятайте, що перші два терміни серійної форми складають 1s. Напр .: 1, 1, 2, 3, 5, 8, 13 ....
Ви хочете, щоб список, що містить номери вирівнень, ініціалізувався лише один раз, після чого він повинен бути оновлений. Тепер ми можемо використовувати__call__
функціонал. Прочитайте відповідь верми @mudit. Це як ви хочете, щоб об'єкт можна було викликати як функцію, але не повторно ініціалізувати кожного разу, коли ви його викликаєте.
Наприклад:
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__
метод на користь реалізації декораторів .
Цей приклад взятий із шаблонів, рецептів та ідіом 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")
Вихід :
Отже, __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
__init__
це спеціальний метод у класах Python, це метод конструктора для класу. Він називається всякий раз, коли об’єкт класу будується або ми можемо сказати, що він ініціалізує новий об'єкт. Приклад:
In [4]: class A:
...: def __init__(self, a):
...: print(a)
...:
...: a = A(10) # An argument is necessary
10
Якщо ми використовуємо (), він видасть помилку
, так TypeError: __init__() missing 1 required positional argument: 'a'
як це вимагає 1 аргумент a
з - за __init__
.
........
__call__
при впровадженні в Клас допомагає нам викликати екземпляр 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__
тут функції.
__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'
>>>
Короткі та солодкі відповіді вже надані вище. Я хочу надати практичну реалізацію порівняно з 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 ми знову створюємо ще один новий екземпляр1 . У сценарії2 ми просто модифікуємо вже створений екземпляр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;
}
}
}
Ми можемо використовувати метод виклику, щоб використовувати інші методи класу як статичні методи.
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")
__init__
метод використовується , коли клас викликається для ініціалізації примірника, в той час як__call__
метод викликається , коли екземпляр називається