Яка різниця між __init__ та __call__?


554

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

Наприклад:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

Відповіді:


754

Перший використовується для ініціалізації новоствореного об'єкта і отримує аргументи, які використовуються для цього:

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) закликає клас робити те, що робить цей клас.
користувач1270710

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 функції є першокласними об'єктами, це означає: посилання на функції можуть передаватися у введеннях до інших функцій та / або методів та виконуватись всередині них.

Екземпляри класів (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

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

Я думаю, це має бути прийнятою відповіддю. Він відповідає точно.
Prasad Raghavendra

18

__init__трактується як конструктор, де як __call__методи можна викликати об'єкти будь-яку кількість разів. І функції, __init__і __call__функції приймають аргументи за замовчуванням.


1
__init__не є конструкторською функцією, але __new__є. __init__називається одразу після__new__
dallonsi

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

13

Я спробую пояснити це на прикладі, припустимо, ви хотіли надрукувати фіксовану кількість термінів із серій фіналів. Пам’ятайте, що перші два терміни серійної форми складають 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__був викликаний лише один раз, саме тоді клас був вперше інстанційований, пізніше об'єкт викликався без повторної ініціалізації.


7

Ви також можете використовувати __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")

Вихід :

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


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 спасибо broh
Ayemun

4

__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__тут функції.


передача об'єкта ініціалізованому об'єкту класу. Отже, об'єкт, що викликається?
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 ми знову створюємо ще один новий екземпляр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;
        }
    }
}

3
Порівняння з Java повністю виходить за рамки питання. У вашому прикладі ви не бачите різниці, оскільки вона була погано вибрана, номери однакові.
Aquiles Carattino

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.