В чем разница между __init__ и __call__?


555

Я хочу знать разницу между __init__и __call__методы.

Например:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

Ответы:


755

Первый используется для инициализации вновь созданного объекта и получает аргументы, используемые для этого:

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__

433
Таким образом, __init__метод используется, когда класс вызывается для инициализации экземпляра, а __call__метод вызывается, когда вызывается экземпляр
pratikm

1
Это кажется правильным, очевидно переменные экземпляра могут быть изменены в течение срока службы экземпляра, что может быть полезно в некоторых случаях.
Мерфи

5
init вызывается при создании экземпляра класса: myfoo = Foo (1,4,7.8) вызов - это шаблон для вызова уже созданного экземпляра класса, чтобы сделать что-то, скажем, класс Foo: \ def __call __ (self, zzz) Затем myfoo (12) вызывает класс, чтобы сделать то, что делает этот класс.
user1270710

1
Просто примечание: поскольку функции в целом являются вызываемыми, они всегда поддерживают вызов метода . Таким образом, вы можете вызывать функцию также так: myFun .__ call __ (arg)
Arindam Roychowdhury

8
Что такое практическое использование __call__?
mrgloom

262

Определение пользовательского __call__()метода в метаклассе позволяет вызывать экземпляр класса как функцию, не всегда изменяя сам экземпляр.

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

In [2]: a = A()
init

In [3]: a()
call

2
__call__не только позволяет использовать экземпляр в качестве функции ... он определяет тело функции, которая выполняется, когда экземпляр используется в качестве функции.
Артур

85

В Python функции являются объектами первого класса, это означает: ссылки на функции могут быть переданы во входные данные для других функций и / или методов и выполнены внутри них.

Экземпляры Классов (или Объекты) могут рассматриваться как функции: передавайте их другим методам / функциям и вызывайте их. Для этого __call__функция класса должна быть специализированной.

def __call__(self, [args ...]) Он принимает в качестве входных данных переменное количество аргументов. Предполагается, xчто он является экземпляром класса X, x.__call__(1, 2)аналогичен вызову x(1,2)или самому экземпляру как функции .

В Python __init__()правильно определен как конструктор классов (равно как __del__()и деструктор классов). Следовательно, между __init__()и существует чистое различие __call__(): первый создает экземпляр класса 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

2
Я понимаю концепцию, но не особенность изменения ее внутреннего состояния . Если мы в приведенном выше коде def __call__просто заменим на def update, мы дадим классу updateметод, который делает то же самое. Теперь он также может изменять внутреннее состояние, если ниже вызывается как s.update(7, 8). Так что же, это __call__просто сахар Синтактикс?
Алекс Повел

27

__call__делает экземпляр класса вызываемым. Зачем это нужно?

Технически __init__вызывается один раз __new__при создании объекта, чтобы его можно было инициализировать.

Но есть много сценариев, когда вы можете захотеть переопределить свой объект, сказать, что вы закончили с вашим объектом и, возможно, найдете потребность в новом объекте. С помощью __call__него можно переопределить тот же объект, как если бы он был новым.

Это только один случай, может быть еще много.


9
Для этого конкретного случая, мы не должны просто создать новый экземпляр? Эффективно ли это каким-то образом модифицировать и использовать один и тот же экземпляр.
Мерфи

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

Я думаю, что это должен быть принятый ответ. Это точно отвечает.
Прасад Рагхавендра

18

__init__будет рассматриваться как конструктор, где __call__методы могут вызываться с объектами любое количество раз. Обе функции __init__и __call__функции принимают аргументы по умолчанию.


1
__init__не является функцией конструктора, но __new__есть. __init__называется сразу после__new__
Даллонси

Я думаю, __new__создает экземпляр класса и получает класс в качестве аргумента, в то __init__время как конструктор экземпляра, поэтому он получает self. Простой способ убедиться в этом заключается в вызове a = Foo(1,2,3)функции, которая получит аргументы конструктора __init__.
Coffee_fan

13

Я попытаюсь объяснить это на примере, предположим, что вы хотите напечатать фиксированное число терминов из ряда Фибоначчи. Помните, что первые 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__вызывался только один раз, когда экземпляр класса создавался впервые, позже объект вызывался без повторной инициализации.


7

Вы также можете использовать __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")

Выход :

введите описание изображения здесь


4
Не могли бы вы скопировать вывод в виде текста?
Соломон Уцко

В чем смысл этого подхода? Можете ли вы противопоставить это другому подходу?
nealmcb

7

Таким образом, __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
@Redowan Delowar спасибо, братан
Аемун Хоссейн Ашик

4

__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__функции.


Передача объекта в инициализированный объект класса. Итак, вызываемый объект?
Ciasto piekarz

3

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

3

Короткие и приятные ответы уже приведены выше. Я хочу предоставить некоторую практическую реализацию по сравнению с 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;
        }
    }
}

3
Сравнение с Java полностью выходит за рамки вопроса. В вашем примере вы не видите никакой разницы, потому что он был плохо выбран, цифры одинаковы.
Аквилес Караттино

2

Мы можем использовать метод вызова, чтобы использовать другие методы класса в качестве статических методов.

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")  
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.