Risposte:
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__
__call__
?
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
__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.
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 x
sia 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
def __call__
semplicemente con def update
, diamo alla classe un update
metodo 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?
__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.
>>> 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 ...
>>>
__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.
__init__
non è una funzione di costruzione ma lo __new__
è. __init__
viene chiamato subito dopo__new__
__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__
.
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.
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 :
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
__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 a
causa 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.
__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'
>>>
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;
}
}
}
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")
__init__
metodo viene utilizzato quando la classe viene chiamato per inizializzare l'istanza, mentre il__call__
metodo viene chiamato quando l' istanza viene chiamata