Was ist der Unterschied zwischen __init__ und __call__ in Python?



Answers

Durch Definieren einer benutzerdefinierten __call__() -Methode in der Meta-Klasse kann die Instanz der Klasse als Funktion aufgerufen werden, wobei die Instanz 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
Question

Ich möchte wissen, was der Unterschied zwischen __init__ und __call__ Methoden ist.

Beispielsweise:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20



__init__ wird als Konstruktor behandelt, wobei __call__ Methoden beliebig oft mit Objekten __call__ werden können. __call__ Funktionen __init__ und __call__ übernehmen Standardargumente.




Kurze und süße Antworten sind bereits oben angegeben. Ich möchte eine praktische Implementierung im Vergleich zu Java bieten.

 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 Szenario 1 erstellen wir jedoch erneut eine neue Instanz instance1 . In scenario2 modifizieren wir einfach bereits erstellte instance1 . __call__ 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;
        }
    }
}



Sie können auch die Methode __call__ verwenden, um decorators implementieren.

Dieses Beispiel stammt aus Python 3 Patterns, Recipes und 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")

Ausgabe :




In Python sind Funktionen erstklassige Objekte, das heißt: Funktionsreferenzen können in Eingaben an andere Funktionen und / oder Methoden übergeben und von dort aus ausgeführt werden.

Instanzen von Klassen (auch bekannt als Objekte) können so behandelt werden, als wären sie Funktionen: Übergeben Sie sie anderen Methoden / Funktionen und rufen Sie sie auf. Um dies zu erreichen, muss die __call__ spezialisiert sein.

def __call__(self, [args ...]) Es wird eine variable Anzahl von Argumenten eingegeben. Unter der Annahme, dass x eine Instanz der Klasse X , ist x.__call__(1, 2) analog zum Aufruf von x(1,2) oder der Instanz selbst als Funktion .

In Python ist __init__() ordnungsgemäß als Klassenkonstruktor definiert (und __del__() ist der Klassenauslöser). Daher gibt es eine Unterscheidung zwischen __init__() und __call__() : Der erste erstellt eine Instanz von Class up, der zweite macht eine Instanz aufrufbar, da eine Funktion den Lebenszyklus des Objekts selbst nicht beeinflussen würde (dh __call__ wirkt sich nicht aus der Lebenszyklus der Konstruktion / Destruktion), 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(7, 8)
__call__ with (7,8)



Ich werde versuchen, dies anhand eines Beispiels zu erklären, angenommen, Sie wollten eine feste Anzahl von Begriffen aus der Fibonacci-Reihe drucken. Denken Sie daran, dass die ersten 2 Begriffe der Fibonacci-Serie 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 werden sollte. Jetzt können wir die __call__ Funktionalität verwenden. Lies @mudit vermas Antwort. Es ist, als ob Sie möchten, dass das Objekt als Funktion aufrufbar ist, aber nicht neu initialisiert wird, wenn Sie es aufrufen.

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 beobachten, dass die Ausgabe __init__ nur einmal __init__ wurde, als die Klasse zum ersten Mal instanziiert wurde, wurde das Objekt später ohne __init__ aufgerufen.




Links



Tags

python python