Was ist der Unterschied zwischen __init__ und __call__?


554

Ich möchte den Unterschied zwischen __init__und __call__Methoden kennen.

Zum Beispiel:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

Antworten:


754

Das erste wird zum Initialisieren eines neu erstellten Objekts verwendet und empfängt dazu die folgenden Argumente:

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

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

Der zweite implementiert einen Funktionsaufrufoperator.

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

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

433
Die __init__Methode wird also verwendet, wenn die Klasse aufgerufen wird, um die Instanz zu initialisieren, während die __call__Methode aufgerufen wird, wenn die Instanz aufgerufen wird
pratikm

1
Das scheint richtig zu sein, anscheinend können Instanzvariablen während der Lebensdauer einer Instanz geändert werden, was in einigen Fällen von Vorteil sein kann.
Murphy

5
init wird aufgerufen, wenn die Klasse instanziiert wird: myfoo = Foo (1,4,7.8) call ist eine Vorlage zum Aufrufen der bereits instanziierten Klasse, um etwas zu tun, sagen wir Klasse Foo: \ def __call __ (self, zzz) Dann myfoo (12) ruft die Klasse auf, um das zu tun, was diese Klasse tut.
user1270710

1
Nur ein Hinweis: Da Funktionen im Allgemeinen sind Callables, sie immer die Methode unterstützt Anruf . Sie können die Funktion also auch so aufrufen: myFun .__ call __ (arg)
Arindam Roychowdhury

8
Was ist die praktische Verwendung von __call__?
Mrgloom

262

Durch das Definieren einer benutzerdefinierten __call__()Methode in der Metaklasse kann die Instanz der Klasse als Funktion aufgerufen werden, wobei die Instanz selbst nicht immer geändert wird.

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__Ermöglicht nicht nur die Verwendung einer Instanz als Funktion, sondern definiert auch den Funktionskörper, der ausgeführt wird, wenn eine Instanz als Funktion verwendet wird.
Arthur

85

In Python sind Funktionen erstklassige Objekte. Dies bedeutet: Funktionsreferenzen können in Eingaben an andere Funktionen und / oder Methoden übergeben und von dort ausgeführt werden.

Instanzen von Klassen (auch Objekte genannt) können so behandelt werden, als wären sie Funktionen: Übergeben Sie sie an andere Methoden / Funktionen und rufen Sie sie auf. Um dies zu erreichen, wird die__call__ Klassenfunktion spezialisiert werden.

def __call__(self, [args ...]) Als Eingabe wird eine variable Anzahl von Argumenten verwendet. Die Annahme x, eine Instanz der Klasse zu sein X, entspricht x.__call__(1, 2)dem Aufruf x(1,2)oder der Instanz selbst als Funktion .

Ist in Python __init__()ordnungsgemäß als Klassenkonstruktor definiert (ebenso wie __del__()der Klassenzerstörer). Daher gibt es eine Nettounterscheidung zwischen __init__()und __call__(): Die erste erstellt eine Instanz von Class up, die zweite macht eine solche Instanz als Funktion aufrufbar , ohne den Lebenszyklus des Objekts selbst zu beeinflussen (dh __call__hat keinen Einfluss auf den Konstruktions- / Zerstörungslebenszyklus), aber es kann seinen internen Zustand ändern (wie unten gezeigt).

Beispiel.

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
Ich verstehe das Konzept, aber nicht die Besonderheit, seinen internen Zustand zu ändern . Wenn wir im obigen Code def __call__einfach durch ersetzen def update, geben wir der Klasse eine updateMethode, die dasselbe tut. Es kann jetzt auch den internen Status ändern, wenn es unten als aufgerufen wird s.update(7, 8). Also, ist dann __call__nur Syntactix Zucker?
Alex Povel

27

__call__macht die Instanz einer Klasse aufrufbar. Warum sollte es erforderlich sein?

Wird technisch __init__einmal beim __new__Erstellen des Objekts aufgerufen , damit es initialisiert werden kann.

Es gibt jedoch viele Szenarien, in denen Sie Ihr Objekt möglicherweise neu definieren möchten, beispielsweise sagen, dass Sie mit Ihrem Objekt fertig sind und möglicherweise ein neues Objekt benötigen. Mit können __call__Sie dasselbe Objekt neu definieren, als wäre es neu.

Dies ist nur ein Fall, es kann noch viel mehr geben.


9
Sollten wir für diesen speziellen Fall nicht einfach eine neue Instanz erstellen? Ist dies in irgendeiner Weise effizient, um dieselbe Instanz zu ändern und zu verwenden?
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 ... 
>>> 

Ich denke, das sollte die akzeptierte Antwort sein. Es antwortet genau.
Prasad Raghavendra

18

__init__würde als Konstruktor behandelt, wobei als __call__Methoden beliebig oft mit Objekten aufgerufen werden kann. Beide __init__und __call__Funktionen verwenden Standardargumente.


1
__init__ist keine Konstruktorfunktion, __new__ist es aber . __init__wird direkt nach__new__
Dallas

Ich denke, __new__erstellt die Klasseninstanz und empfängt eine Klasse als Argument, während dies __init__der Instanzkonstruktor ist, weshalb er empfängt self. Ein einfacher Weg, dies zu sehen, ist der Aufruf a = Foo(1,2,3)der Funktion, die die Konstruktorargumente empfängt __init__.
Coffee_fan

13

Ich werde versuchen, dies anhand eines Beispiels zu erklären. Angenommen, Sie möchten eine feste Anzahl von Begriffen aus Fibonacci-Reihen drucken. Denken Sie daran, dass die ersten beiden Terme der Fibonacci-Reihe 1s sind. ZB: 1, 1, 2, 3, 5, 8, 13 ....

Sie möchten, dass die Liste mit den Fibonacci-Nummern nur einmal initialisiert wird und danach aktualisiert wird. Jetzt können wir die __call__Funktionalität nutzen. Lesen Sie die Antwort von @mudit verma. Es ist so, als ob Sie möchten, dass das Objekt als Funktion aufgerufen, aber nicht bei jedem Aufruf neu initialisiert werden kann.

Z.B:

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)

Die Ausgabe ist:

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__

Wenn Sie feststellen, dass die Ausgabe __init__nur einmal aufgerufen wurde, als die Klasse zum ersten Mal instanziiert wurde, wurde das Objekt später ohne erneute Initialisierung aufgerufen.


7

Sie können die __call__Methode auch zugunsten der Implementierung von Dekorateuren verwenden .

Dieses Beispiel stammt aus Python 3-Mustern, Rezepten und Redewendungen

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

Ausgabe :

Geben Sie hier die Bildbeschreibung ein


4
Könnten Sie bitte die Ausgabe als Text kopieren?
Solomon Ucko

Was ist der Sinn dieses Ansatzes? Können Sie es einem anderen Ansatz gegenüberstellen?
Nealmcb

7

Damit, __init__ also aufgerufen, wenn Sie eine Instanz einer Klasse erstellen und auch die Instanzvariable initialisieren.

Beispiel:

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

Und __call__ wird aufgerufen, wenn Sie das Objekt wie jede andere Funktion aufrufen.

Beispiel:

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 danke broh
Ayemun Hossain Ashik

4

__init__ist eine spezielle Methode in Python-Klassen und die Konstruktormethode für eine Klasse. Es wird immer dann aufgerufen, wenn ein Objekt der Klasse erstellt wird oder wenn wir sagen können, dass es ein neues Objekt initialisiert. Beispiel:

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

Wenn wir A () verwenden, wird ein Fehler TypeError: __init__() missing 1 required positional argument: 'a'ausgegeben, ada aufgrund von 1 Argument erforderlich ist __init__.

........

__call__ Wenn es in der Klasse implementiert ist, können wir die Klasseninstanz als Funktionsaufruf aufrufen.

Beispiel:

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

Wenn wir hier B () verwenden, läuft es einwandfrei, da es hier keine __init__Funktion hat.


Übergeben eines Objekts an ein initialisiertes Klassenobjekt. Also ein aufrufbares Objekt?
Ciasto piekarz

3

__call__Ermöglicht die Rückgabe beliebiger Werte, während __init__ein Konstruktor implizit die Instanz der Klasse zurückgibt. Wie andere Antworten richtig hervorgehoben haben, __init__wird es nur einmal aufgerufen, während es möglich ist, __call__mehrere Male aufzurufen , falls die initialisierte Instanz einer Zwischenvariablen zugewiesen ist.

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

Kurze und süße Antworten finden Sie oben bereits. Ich möchte eine praktische Implementierung im Vergleich zu Java bereitstellen.

 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

Hinweis : Szenario 1 und Szenario 2 scheinen in Bezug auf die Ergebnisausgabe gleich zu sein. In Szenario1 erstellen wir erneut eine neue Instanzinstanz1 . In Szenario 2 ändern wir einfach die bereits erstellte Instanz1 . __call__Dies ist hier von Vorteil, da das System keine neue Instanz erstellen muss.

Entspricht in 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
Ein Vergleich mit Java ist völlig ausgeschlossen. In Ihrem Beispiel sehen Sie keinen Unterschied, da es schlecht ausgewählt wurde und die Zahlen gleich sind.
Aquiles Carattino

2

Wir können die Aufrufmethode verwenden, um andere Klassenmethoden als statische Methoden zu verwenden.

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")  
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.