Je veux connaître la différence entre les méthodes __init__
et __call__
.
Par exemple:
class test:
def __init__(self):
self.a = 10
def __call__(self):
b = 20
Le premier est utilisé pour initialiser un objet nouvellement créé et reçoit les arguments utilisés pour le faire:
class Foo:
def __init__(self, a, b, c):
# ...
x = Foo(1, 2, 3) # __init__
Le second implémente l'opérateur d'appel de fonction.
class Foo:
def __call__(self, a, b, c):
# ...
x = Foo()
x(1, 2, 3) # __call__
La définition d'une méthode personnalisée __call__()
dans la méta-classe permet à l'instance de la classe d'être appelée en tant que fonction, mais ne modifie pas toujours l'instance elle-même.
In [1]: class A:
...: def __init__(self):
...: print "init"
...:
...: def __call__(self):
...: print "call"
...:
...:
In [2]: a = A()
init
In [3]: a()
call
En Python, les fonctions sont des objets de première classe, ce qui signifie que les références de fonction peuvent être transmises en entrée à d'autres fonctions et/ou méthodes et exécutées de l'intérieur.
Les instances de classes (ou objets) peuvent être traitées comme si elles étaient des fonctions: transmettez-les à d'autres méthodes/fonctions et appelez-les. Pour ce faire, la fonction de classe __call__
doit être spécialisée.
def __call__(self, [args ...])
Il prend en entrée un nombre variable d'arguments. Supposer que x
soit une instance de la classe X
, x.__call__(1, 2)
est analogue à l'appel de x(1,2)
ou l'instance elle-même en tant que fonction.
En Python, __init__()
est correctement défini en tant que constructeur de classe (ainsi que __del__()
est le destructeur de classe). Par conséquent, il existe une distinction nette entre __init__()
et __call__()
: le premier construit une instance de Class up, le second rend cette instance appelable en tant que fonction n’aurait aucune incidence sur le cycle de vie de l’objet lui-même (c’est-à-dire que __call__
n’a aucune incidence sur le cycle de vie de la construction/destruction), mais elle peut modifier son état interne (comme indiqué ci-dessous).
Exemple.
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
__call__
rend l'instance d'une classe appelable. Pourquoi serait-il nécessaire?
Techniquement, __init__
est appelé une fois par __new__
lors de la création de l'objet, afin qu'il puisse être initialisé.
Mais il existe de nombreux scénarios dans lesquels vous pourriez vouloir redéfinir votre objet, par exemple en avoir terminé avec votre objet et trouver éventuellement le besoin d’un nouvel objet. Avec __call__
vous pouvez redéfinir le même objet comme s'il était neuf.
Ce n'est qu'un cas, il peut y en avoir beaucoup d'autres.
__init__
serait traité comme un constructeur où les méthodes __call__
pourraient être appelées avec des objets autant de fois que nécessaire. Les deux fonctions __init__
et __call__
prennent les arguments par défaut.
>>> 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 ...
>>>
Je vais essayer d’expliquer cela à l’aide d’un exemple. Supposons que vous vouliez imprimer un nombre fixe de termes de la série Fibonacci. Rappelez-vous que les 2 premiers termes de la série de fibonacci sont des 1. Ex: 1, 1, 2, 3, 5, 8, 13 ....
Vous souhaitez que la liste contenant les numéros de fibonacci ne soit initialisée qu'une seule fois et qu'elle soit mise à jour ensuite. Nous pouvons maintenant utiliser la fonctionnalité __call__
. Lisez la réponse de @mudit verma. C'est comme si vous vouliez que l'objet soit appelable en tant que fonction mais ne soit pas réinitialisé à chaque fois que vous l'appelez.
Par exemple:
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)
La sortie est:
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__
Si vous observez que la sortie __init__
n'a été appelée qu'une fois, c'est-à-dire lorsque la classe a été instanciée pour la première fois, l'objet a ensuite été appelé sans être réinitialisé.
Vous pouvez également utiliser la méthode __call__
en faveur de la mise en œuvre de décorateurs .
Cet exemple tiré de 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")
Sortie :
__init__
est une méthode spéciale dans Python classes, c'est la méthode du constructeur pour une classe. On l'appelle à chaque fois qu'un objet de la classe est construit ou qu'on peut dire qu'il initialise un nouvel objet. Exemple:
In [4]: class A:
...: def __init__(self, a):
...: print(a)
...:
...: a = A(10) # An argument is necessary
10
Si nous utilisons A (), cela donnera une erreur TypeError: __init__() missing 1 required positional argument: 'a'
car il nécessite 1 argument a
à cause de __init__
.
........
__call__
lorsqu'il est implémenté dans la classe nous aide à appeler l'instance de classe en tant qu'appel de fonction.
Exemple:
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
Ici, si nous utilisons B (), cela fonctionne très bien car il n’a pas de fonction __init__
.
Nous pouvons utiliser la méthode call pour utiliser d'autres méthodes de classe en tant que méthodes statiques.
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")
__call__
permet de renvoyer des valeurs arbitraires, tandis que __init__
étant un constructeur, l'instance de la classe est renvoyée implicitement. Comme d'autres réponses l'ont bien souligné, __init__
n'est appelé qu'une seule fois, alors qu'il est possible d'appeler __call__
plusieurs fois, au cas où l'instance initialisée serait affectée à une variable intermédiaire.
>>> 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'
>>>
Des réponses courtes et douces sont déjà fournies ci-dessus. Je veux fournir une implémentation pratique par rapport à 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
Note: les scénarios 1 et 2 semblent identiques en termes de résultat. Mais dans scenario1, nous créons à nouveau une autre nouvelle instance instance1. Dans le scénario2, nous modifions simplement les éléments déjà créés instance1. __call__
est utile dans la mesure où le système n'a pas besoin de créer une nouvelle instance.
équivalent en 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;
}
}
}