В чем разница между __init__ и __call__ в Python?
Я хочу знать, какая разница между __init__
а также __call__
методы есть.
Например:
class test:
def __init__(self):
self.a = 10
def __call__(self):
b = 20
18 ответов
Первый используется для инициализации вновь созданного объекта и получает аргументы, используемые для этого:
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__()
правильно определен как конструктор класса (а также __del__()
Класс Разрушитель). Следовательно, существует чистое различие между __init__()
а также __call__()
: первый создает экземпляр Class вверх, второй делает такой экземпляр вызываемым, поскольку функция не влияет на жизненный цикл самого объекта (т.е. __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 ...
>>>
__init__
будет рассматриваться как конструктор, где как __call__
методы могут быть вызваны с объектами любое количество раз. И то и другое __init__
а также __call__
функции принимают аргументы по умолчанию.
Я попытаюсь объяснить это на примере, предположим, что вы хотите напечатать фиксированное число терминов из ряда Фибоначчи. Помните, что первые 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'
>>>
Так, __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
Вы также можете использовать __call__
метод в пользу реализации декораторов.
Этот пример взят из Python 3 Patterns, Recipes and 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")
Выход:
Дело 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)
Запустить:
Результат:
- 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
Короткие и приятные ответы уже приведены выше. Я хочу предоставить некоторую практическую реализацию по сравнению с 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 мы просто модифицируем уже созданный экземпляр 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 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
Теперь мы создаем и инициализируем экземпляр класса, как показано ниже:
# Here
obj = Person("John", "Smith")
Затем вызывается, как показано ниже:
"__init__()" is called.
Затем мы вызываем двумя способами, как показано ниже:
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
И может вызываться много раз, как показано ниже:
obj = Person("John", "Smith")
print(obj.__init__("Tom", "Brown")) # Here
print(obj("Hello, "))
print(obj.__call__("Hello, "))
Затем вызывается, и экземпляр класса повторно инициализируется и возвращается, как показано ниже:
"__init__()" is called.
"__init__()" is called. # Here
None # Here
Hello, Tom Brown
Hello, Tom Brown
И, если не возвращается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'
И если__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: объект «Человек» не может быть вызван
Потом снова звонимobj.__call__("Hello, ")
как показано ниже:
obj = Person("John", "Smith")
obj.__call__("Hello, ") # Here
Возникает следующая ошибка:
AttributeError: объект «Человек» не имеет атрибута «__call__»
__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__
при реализации в классе помогает нам вызывать экземпляр класса как вызов функции.
Пример:
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__
функции здесь.
Мы можем использовать метод вызова, чтобы использовать другие методы класса в качестве статических методов.
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")
Я хочу предложить несколько сокращений и синтаксического сахара, а также несколько методов, которые можно использовать, но я не вижу их в текущих ответах.
Создайте экземпляр класса и немедленно вызовите его
Во многих случаях, например, когда нужно сделать запрос 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")
Give для вызова другого существующего метода
Мы также можем объявить метод без создания фактического
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")
Метод 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 ... "
Метод __ init__ запускается автоматически при создании объекта. Он используется для инициализации переменных экземпляра.
class my_class():
def __init__(self,a,b):
self.a = a
self.b = b
print("Object was created, instance variables were initialized")
obj = my_class(1,2)
print(obj.a) #prints 1
print(obj.b) #prints 2
Метод __ call__ может использоваться для переопределения / повторной инициализации тех же объектов. Это также облегчает использование экземпляров / объектов класса в качестве функций путем передачи аргументов объектам.
class my_class():
def __init__(self, a,b):
self.a=a
self.b=b
def __call__(self,a,b):
Sum = a+b
return Sum
obj = my_class(1,2) # a=1, b=2
Sum = obj(4,5) # a=4, b=5 instance variables are re-initialized
print(Sum) # 4 + 5= 9