Jaka jest różnica między __init__ a __call__?

554

Chcę znać różnicę między __init__i __call__metod.

Na przykład:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20
sam
źródło

Odpowiedzi:

754

Pierwszy służy do inicjalizacji nowo utworzonego obiektu i odbiera argumenty użyte do tego:

class Foo:
    def __init__(self, a, b, c):
        # ...

x = Foo(1, 2, 3) # __init__

Drugi implementuje operator wywołania funkcji.

class Foo:
    def __call__(self, a, b, c):
        # ...

x = Foo()
x(1, 2, 3) # __call__
Cat Plus Plus
źródło
433
Tak więc __init__metoda jest używana, gdy klasa jest wywoływana w celu zainicjowania instancji, podczas gdy __call__metoda jest wywoływana, gdy instancja jest wywoływana
pratikm
1
To wydaje się poprawne, najwyraźniej zmienne instancji mogą być modyfikowane w trakcie trwania instancji, co może być korzystne w niektórych przypadkach.
Murphy
5
startowych jest wywoływana podczas tworzenia instancji klasy: myfoo = Foo (1,4,7.8) wezwanie jest szablon do wywołania już instancję klasy coś zrobić Załóżmy, powiedzmy klasy Foo: \ def __call __ (self, zzz) Następnie myfoo (12) wzywa klasę do zrobienia tego, co robi klasa.
user1270710,
1
Tylko uwaga: ponieważ funkcje są na ogół wywoływalne, zawsze obsługują wywołanie metody . Możesz więc wywołać funkcję również w ten sposób: myFun .__ call __ (arg)
Arindam Roychowdhury
8
Jakie jest praktyczne zastosowanie __call__?
mrgloom
262

Zdefiniowanie niestandardowej __call__()metody w meta-klasie pozwala na wywołanie instancji klasy jako funkcji, nie zawsze modyfikując samą instancję.

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

In [2]: a = A()
init

In [3]: a()
call
avasal
źródło
2
__call__nie tylko pozwala na użycie instancji jako funkcji ... definiuje ciało funkcji, które jest wykonywane, gdy instancja jest używana jako funkcja.
Arthur
85

W Pythonie funkcje są pierwszorzędnymi obiektami, co oznacza: odwołania do funkcji mogą być przekazywane w danych wejściowych do innych funkcji i / lub metod i wykonywane z ich wnętrza.

Instancje klas (zwane też obiektami) można traktować tak, jakby były funkcjami: przekaż je innym metodom / funkcjom i wywołaj je. Aby to osiągnąć, __call__funkcja klasy musi być wyspecjalizowana.

def __call__(self, [args ...]) Jako dane wejściowe przyjmuje zmienną liczbę argumentów. Zakładanie, xże jest instancją klasy X, x.__call__(1, 2)jest analogiczne do wywołania x(1,2)lub samej instancji jako funkcji .

W Pythonie __init__()jest poprawnie zdefiniowany jako Class Constructor (podobnie jak __del__()Class Destructor). Dlatego istnieje wyraźne rozróżnienie między __init__()i __call__(): pierwszy buduje instancję klasy w górę, drugi sprawia, że ​​taka instancja jest wywoływalna jako funkcja bez wpływu na cykl życia samego obiektu (tj.__call__ nie wpływałaby na cykl życia Nie ma wpływu na cykl budowy / zniszczenia), ale może modyfikować swój stan wewnętrzny (jak pokazano poniżej).

Przykład.

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
Paolo Maresca
źródło
2
Rozumiem pojęcie, ale nie specjalną cechę zmiany jego stanu wewnętrznego . Jeśli w powyższym kodzie zastąpimy def __call__po prostu def update, dajemy klasie updatemetodę, która robi to samo. Może teraz również modyfikować stan wewnętrzny, jeśli zostanie wywołany poniżej jako s.update(7, 8). Czy to zatem __call__tylko cukier syntactix?
Alex Povel
27

__call__umożliwia wywołanie klasy. Dlaczego byłoby to wymagane?

Technicznie __init__jest wywoływany raz, __new__kiedy obiekt jest tworzony, aby można go było zainicjować.

Ale istnieje wiele scenariuszy, w których możesz chcieć ponownie zdefiniować swój obiekt, powiedzieć, że skończyłeś z tym obiektem i może okazać się potrzebny nowy obiekt. Za pomocą __call__możesz ponownie zdefiniować ten sam obiekt, jakby był nowy.

To tylko jeden przypadek, może być ich znacznie więcej.

Mudit Verma
źródło
9
Czy w tym konkretnym przypadku nie powinniśmy po prostu utworzyć nowej instancji? Czy jest to w jakiś sposób skuteczne modyfikowanie i używanie tego samego wystąpienia?
Murphy
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 ... 
>>> 
Praca w
źródło
Myślę, że to powinna być zaakceptowana odpowiedź. Odpowiada dokładnie.
Prasad Raghavendra
18

__init__byłby traktowany jako Konstruktor, gdzie jako __call__metody można wywoływać obiekty dowolną liczbę razy. Obie __init__i __call__funkcje przyjmują domyślne argumenty.

Vikram
źródło
1
__init__nie jest funkcją konstruktora, ale __new__jest. __init__nazywa się zaraz po__new__
dallonsi
Myślę, że __new__tworzy instancję klasy i przyjmuje klasę jako argument, podczas gdy __init__jest konstruktorem instancji i dlatego ją otrzymuje self. Łatwym sposobem na sprawdzenie tego jest wywołanie a = Foo(1,2,3)funkcji, która otrzyma argumenty konstruktora __init__.
Coffee_fan
13

Spróbuję to wyjaśnić na przykładzie, załóżmy, że chcesz wydrukować określoną liczbę terminów z serii Fibonacciego. Pamiętaj, że pierwsze 2 terminy serii Fibonacciego to 1. Np .: 1, 1, 2, 3, 5, 8, 13 ....

Chcesz, aby lista zawierająca liczby Fibonacciego była inicjowana tylko raz, a następnie powinna zostać zaktualizowana. Teraz możemy korzystać z __call__funkcjonalności. Przeczytaj odpowiedź @mudit verma. To tak, jakbyś chciał, aby obiekt był wywoływalny jako funkcja, ale nie był inicjowany przy każdym wywołaniu.

Na przykład:

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)

Dane wyjściowe to:

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__

Jeśli zauważysz, że dane wyjściowe __init__zostały wywołane tylko raz, to wtedy klasa została utworzona po raz pierwszy, później obiekt został wywołany bez ponownej inicjalizacji.

Ruthvik Vaila
źródło
7

Możesz także użyć __call__metody na rzecz implementacji dekoratorów .

Ten przykład pochodzi z wzorców, przepisów i idiomów języka 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")

Wyjście :

wprowadź opis zdjęcia tutaj

Teoman Shipahi
źródło
4
Czy możesz skopiować wynik jako tekst?
Solomon Ucko
Jaki jest sens tego podejścia? Czy możesz to porównać z innym podejściem?
nealmcb
7

Jest więc __init__wywoływany, gdy tworzysz instancję dowolnej klasy i inicjujesz również zmienną instancji.

Przykład:

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")

I __call__jest wywoływany, gdy wywołujesz obiekt jak każdą inną funkcję.

Przykład:

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
Ayemun Hossain Ashik
źródło
1
@Redowan Delowar dzięki bracie
Ayemun Hossain Ashik
4

__init__jest specjalną metodą w klasach Python, jest to metoda konstruktora dla klasy. Jest wywoływany za każdym razem, gdy budowany jest obiekt klasy lub można powiedzieć, że inicjuje nowy obiekt. Przykład:

    In [4]: class A:
   ...:     def __init__(self, a):
   ...:         print(a)
   ...:
   ...: a = A(10) # An argument is necessary
10

Jeśli użyjemy A (), wygeneruje błąd, TypeError: __init__() missing 1 required positional argument: 'a'ponieważ wymaga 1 argumentu z apowodu __init__.

........

__call__ po zaimplementowaniu w klasie pomaga nam wywoływać instancję klasy jako wywołanie funkcji.

Przykład:

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

Tutaj, jeśli użyjemy B (), działa dobrze, ponieważ nie ma tu żadnej __init__funkcji.

bhatnaushad
źródło
przekazanie obiektu do zainicjowanego obiektu klasy. Więc obiekt na żądanie?
Ciasto piekarz
3

__call__pozwala na zwrócenie dowolnych wartości, podczas gdy __init__będąc konstruktorem pośrednio zwraca instancję klasy. Jak słusznie wskazano inne odpowiedzi, __init__wywoływany jest tylko raz, podczas gdy można wywoływać __call__wiele razy, na wypadek, gdyby zainicjowana instancja została przypisana do zmiennej pośredniej.

>>> 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'
>>> 
Dmitrij Sintsov
źródło
3

Krótkie i słodkie odpowiedzi są już podane powyżej. Chcę zapewnić praktyczne wdrożenie w porównaniu z Javą.

 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

Uwaga : scenariusz 1 i scenariusz 2 wydają się takie same pod względem wyników. Ale w scenariuszu 1 ponownie tworzymy kolejną nową instancję instancji1 . W scenariuszu 2 po prostu modyfikujemy już utworzoną instancję1 . __call__jest tutaj korzystne, ponieważ system nie musi tworzyć nowej instancji.

Odpowiednik w Javie

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;
        }
    }
}
Uddhav Gautam
źródło
3
Porównywanie z Javą jest całkowicie poza zakresem pytania. W twoim przykładzie nie widzisz żadnej różnicy, ponieważ został źle wybrany, liczby są takie same.
Aquiles Carattino
2

Możemy użyć metody call , aby użyć innych metod klasowych jako metod statycznych.

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")  
Abhishek Jain
źródło