В чем разница между __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
Другие вопросы по тегам