Qual è la differenza tra __init__ e __call__?


555

Voglio sapere la differenza tra __init__e __call__metodi.

Per esempio:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

Risposte:


755

Il primo viene utilizzato per inizializzare l'oggetto appena creato e riceve gli argomenti utilizzati per farlo:

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

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

Il secondo implementa l'operatore di chiamata di funzione.

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

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

433
Così, il __init__metodo viene utilizzato quando la classe viene chiamato per inizializzare l'istanza, mentre il __call__metodo viene chiamato quando l' istanza viene chiamata
pratikm

1
Ciò sembra corretto, apparentemente le variabili di istanza possono essere modificate durante la vita di un'istanza che può essere utile in alcuni casi.
Murphy,

5
init viene chiamato quando si crea un'istanza della classe: myfoo = Foo (1,4,7,8) call è un modello per chiamare la classe già istanziata per fare qualcosa diciamo classe Foo: \ def __call __ (self, zzz) Quindi, myfoo (12) chiama la classe per fare ciò che fa quella classe.
user1270710

1
Solo una nota: poiché le funzioni in generale sono richiamabili, supportano sempre la chiamata del metodo . Quindi puoi chiamare la funzione anche in questo modo: myFun .__ call __ (arg)
Arindam Roychowdhury

8
A cosa serve l'uso pratico __call__?
MrGloom,

262

La definizione di un __call__()metodo personalizzato nella meta-classe consente di chiamare l'istanza della classe come funzione, non sempre modificando l'istanza stessa.

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__non solo consente di utilizzare un'istanza come funzione ... definisce anche il corpo della funzione che viene eseguito quando un'istanza viene utilizzata come funzione.
Arthur

85

In Python, le funzioni sono oggetti di prima classe, ciò significa che i riferimenti alle funzioni possono essere passati in input ad altre funzioni e / o metodi ed eseguiti al loro interno.

Le Istanze di Classi (aka Oggetti), possono essere trattate come se fossero funzioni: passale ad altri metodi / funzioni e chiamale. Per raggiungere questo obiettivo, la __call__funzione di classe deve essere specializzata.

def __call__(self, [args ...]) Prende come input un numero variabile di argomenti. Supponendo che xsia un'istanza della classe X, x.__call__(1, 2)è analogo alla chiamata x(1,2)o all'istanza stessa come funzione .

In Python, __init__()è correttamente definito come costruttore di classe (così come __del__()il distruttore di classe). Pertanto, esiste una netta distinzione tra __init__()e __call__(): il primo crea un'istanza di Class up, il secondo rende tale istanza richiamabile come una funzione senza influire sul ciclo di vita dell'oggetto stesso (cioè __call__non influisce sul ciclo di vita di costruzione / distruzione) ma può modificare il suo stato interno (come mostrato di seguito).

Esempio.

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
Capisco il concetto, ma non la particolarità di modificarne lo stato interno . Se nel codice precedente sostituiamo def __call__semplicemente con def update, diamo alla classe un updatemetodo che fa la stessa cosa. Ora può anche modificare lo stato interno, se chiamato sotto come s.update(7, 8). Quindi, allora è __call__solo zucchero sintattico?
Alex Povel,

27

__call__rende richiamabile l'istanza di una classe. Perché dovrebbe essere richiesto?

Tecnicamente __init__viene chiamato una volta __new__quando viene creato l'oggetto, in modo che possa essere inizializzato.

Ma ci sono molti scenari in cui potresti voler ridefinire il tuo oggetto, dire che hai finito con il tuo oggetto e che potresti trovare la necessità di un nuovo oggetto. Con __call__te puoi ridefinire lo stesso oggetto come se fosse nuovo.

Questo è solo un caso, ce ne possono essere molti altri.


9
Per questo caso specifico, non dovremmo semplicemente creare una nuova istanza? È efficace in qualche modo modificare e utilizzare la stessa istanza.
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 ... 
>>> 

Penso che questa dovrebbe essere la risposta accettata. Risponde esattamente.
Prasad Raghavendra,

18

__init__verrebbe trattato come un costruttore in cui i __call__metodi possono essere chiamati con oggetti un numero qualsiasi di volte. Entrambe __init__e le __call__funzioni accettano argomenti predefiniti.


1
__init__non è una funzione di costruzione ma lo __new__è. __init__viene chiamato subito dopo__new__
dallonsi il

Penso che __new__crea l'istanza di classe e riceva una classe come argomento, mentre __init__è il costruttore dell'istanza che è il motivo per cui riceve self. Un modo semplice per vederlo è nella chiamata a = Foo(1,2,3)la funzione che riceverà gli argomenti del costruttore __init__.
Coffee_fan

13

Proverò a spiegarlo usando un esempio, supponiamo che tu voglia stampare un numero fisso di termini dalla serie fibonacci. Ricorda che i primi 2 termini della serie fibonacci sono 1s. Ad esempio: 1, 1, 2, 3, 5, 8, 13 ....

Si desidera che l'elenco contenente i numeri di fibonacci sia inizializzato una sola volta e che successivamente si aggiorni. Ora possiamo usare la __call__funzionalità. Leggi la risposta di @mudit verma. È come se volessi che l'oggetto fosse richiamabile come funzione ma non reinizializzato ogni volta che lo chiami.

Per esempio:

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)

L'output è:

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__

Se osservi che l'output è __init__stato chiamato solo una volta, in quel momento la classe è stata istanziata per la prima volta, in seguito l'oggetto è stato chiamato senza reinizializzare.


7

Puoi anche usare il __call__metodo a favore dell'implementazione di decoratori .

Questo esempio è tratto da 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")

Produzione :

inserisci qui la descrizione dell'immagine


4
Potresti copiare l'output come testo?
Solomon Ucko,

Qual è il punto di questo approccio? Riesci a contrastarlo con un approccio diverso?
nealmcb

7

Così, __init__ viene chiamato quando si crea un'istanza di qualsiasi classe e si inizializza anche la variabile di istanza.

Esempio:

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

E __call__ viene chiamato quando si chiama l'oggetto come qualsiasi altra funzione.

Esempio:

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

4

__init__è un metodo speciale nelle classi Python, è il metodo di costruzione per una classe. Viene chiamato ogni volta che viene costruito un oggetto della classe o possiamo dire che inizializza un nuovo oggetto. Esempio:

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

Se utilizziamo A (), verrà visualizzato un errore TypeError: __init__() missing 1 required positional argument: 'a'poiché richiede 1 argomento a acausa di __init__.

........

__call__ quando implementato nella classe ci aiuta a invocare l'istanza della classe come una chiamata di funzione.

Esempio:

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

Qui se usiamo B (), funziona bene perché non ha una __init__funzione qui.


passando un oggetto a un oggetto classe inizializzato. Quindi un oggetto richiamabile?
Ciasto piekarz,

3

__call__consente di restituire valori arbitrari, pur __init__essendo un costruttore restituisce implicitamente l'istanza della classe. Come altre risposte correttamente sottolineato, __init__viene chiamato solo una volta, mentre è possibile chiamare __call__più volte, nel caso in cui l'istanza inizializzata sia assegnata alla variabile intermedia.

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

Le risposte brevi e dolci sono già fornite sopra. Voglio fornire alcune implementazioni pratiche rispetto a 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

Nota : lo scenario 1 e lo scenario 2 sembrano uguali in termini di output dei risultati. Ma nello scenario1, creiamo nuovamente un'altra nuova istanza1 di istanza . In scenario2, abbiamo semplicemente modifichiamo già creato instance1 . __call__è utile qui poiché il sistema non ha bisogno di creare una nuova istanza.

Equivalente 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
Il confronto con Java è completamente fuori dall'ambito della domanda. Nel tuo esempio, non vedi alcuna differenza perché era mal selezionata, i numeri sono gli stessi.
Aquiles Carattino,

2

Possiamo usare il metodo call per usare altri metodi di classe come metodi statici.

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")  
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.