Quelle est la différence entre __init__ et __call__?

555

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
sam
la source

Réponses:

755

Le premier est utilisé pour initialiser l'objet nouvellement créé et reçoit les arguments utilisés pour ce 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__
Cat Plus Plus
la source
433
Ainsi, la __init__méthode est utilisée lorsque la classe est appelée pour initialiser l'instance, tandis que la __call__méthode est appelée lorsque l' instance est appelée
pratikm
1
Cela semble correct, apparemment les variables d'instance peuvent être modifiées au cours de la vie d'une instance, ce qui peut être bénéfique dans certains cas.
Murphy
5
init est appelé lors de l'instanciation de la classe: myfoo = Foo (1,4,7.8) call est un modèle pour appeler la classe déjà instanciée pour faire quelque chose disons la classe Foo: \ def __call __ (self, zzz) Ensuite, myfoo (12) appelle la classe pour faire ce que fait cette classe.
user1270710
1
Juste une note: comme les fonctions sont en général appelables, elles prennent toujours en charge l' appel de méthode . Vous pouvez donc appeler la fonction comme ceci: myFun .__ call __ (arg)
Arindam Roychowdhury
8
Quelle est l'utilisation pratique de __call__?
mrgloom
262

La définition d'une __call__()méthode personnalisée dans la méta-classe permet d'appeler l'instance de la classe en tant que fonction, sans modifier 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
avasal
la source
2
__call__permet non seulement d'utiliser une instance en tant que fonction ... mais définit le corps de la fonction qui est exécutée lorsqu'une instance est utilisée en tant que fonction.
Arthur
85

En Python, les fonctions sont des objets de première classe, cela signifie: les références de fonction peuvent être passées en entrées à d'autres fonctions et / ou méthodes, et exécutées depuis l'intérieur.

Les instances de classes (ou objets) peuvent être traitées comme s'il s'agissait de fonctions: passez-les à d'autres méthodes / fonctions et appelez-les. Pour ce faire, la __call__fonction de classe doit être spécialisée.

def __call__(self, [args ...]) Il prend en entrée un nombre variable d'arguments. En supposant xêtre une instance de la classe X, cela x.__call__(1, 2)revient à appeler x(1,2)ou l'instance elle-même en tant que fonction .

En Python, __init__()est correctement défini comme constructeur de classe (tout comme __del__()le destructeur de classe). Par conséquent, il y a une nette distinction entre __init__()et __call__(): le premier crée une instance de Class up, le second rend cette instance appelable comme une fonction sans impact sur le cycle de vie de l'objet lui-même (c'est __call__-à- dire sans impact sur le cycle de vie de construction / destruction) mais il peut modifier son état interne (comme illustré 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
Paolo Maresca
la source
2
Je comprends le concept, mais pas la particularité de modifier son état interne . Si dans le code ci-dessus nous remplaçons def __call__simplement par def update, nous donnons à la classe une updateméthode qui fait la même chose. Il peut désormais également modifier l'état interne, s'il est appelé ci-dessous comme s.update(7, 8). Alors, c'est __call__juste du sucre syntaxique alors?
Alex Povel
27

__call__rend l'instance d'une classe appelable. Pourquoi serait-il nécessaire?

Techniquement, il __init__est appelé une fois __new__lorsque l'objet est créé, afin qu'il puisse être initialisé.

Mais il existe de nombreux scénarios où vous voudrez peut-être redéfinir votre objet, disons que vous en avez terminé avec votre objet et que vous pourriez avoir besoin d'un nouvel objet. Avec, __call__vous pouvez redéfinir le même objet comme s'il était nouveau.

Ce n'est qu'un cas, il peut y en avoir beaucoup plus.

Mudit Verma
la source
9
Pour ce cas spécifique, ne devrions-nous pas simplement créer une nouvelle instance? Est-ce efficace d'une certaine manière pour modifier et utiliser la même instance.
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 ... 
>>> 
Jobin
la source
Je pense que cela devrait être la réponse acceptée. Il répond précisément.
Prasad Raghavendra
18

__init__serait traité comme un constructeur où les __call__méthodes peuvent être appelées avec des objets un certain nombre de fois. Les fonctions __init__et __call__acceptent les arguments par défaut.

Vikram
la source
1
__init__n'est pas une fonction constructeur mais l' __new__est. __init__est appelé juste après__new__
dallonsi
Je pense que __new__crée l'instance de classe et recevoir une classe comme argument, alors que __init__le constructeur d'instance qui est la raison pour laquelle il reçoit self. Un moyen simple de voir cela est dans l'appel a = Foo(1,2,3)la fonction qui recevra les arguments du constructeur __init__.
Coffee_fan
13

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. N'oubliez pas que les 2 premiers termes de la série fibonacci sont 1s. Par exemple: 1, 1, 2, 3, 5, 8, 13 ....

Vous voulez que la liste contenant les numéros de fibonacci ne soit initialisée qu'une seule fois et ensuite elle devrait être mise à jour. Maintenant, nous pouvons utiliser la __call__fonctionnalité. Lisez la réponse de @mudit verma. C'est comme si vous vouliez que l'objet soit appelable en tant que fonction mais pas réinitialisé à chaque fois que vous l'appeliez.

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 a __init__été appelée une seule fois, c'est à ce moment que la classe a été instanciée pour la première fois, l'objet a ensuite été appelé sans réinitialisation.

Ruthvik Vaila
la source
7

Vous pouvez également utiliser la __call__méthode 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 :

entrez la description de l'image ici

Teoman shipahi
la source
4
Pourriez-vous s'il vous plaît copier la sortie sous forme de texte?
Solomon Ucko
Quel est l'intérêt de cette approche? Pouvez-vous le comparer avec une approche différente?
nealmcb
7

Ainsi, __init__est appelé lorsque vous créez une instance de n'importe quelle classe et initialisez également la variable d'instance.

Exemple:

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

Et __call__est appelé lorsque vous appelez l'objet comme n'importe quelle autre fonction.

Exemple:

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
Ayemun Hossain Ashik
la source
1
@Redowan Delowar merci broh
Ayemun Hossain Ashik
4

__init__est une méthode spéciale dans les classes Python, c'est la méthode constructeur d'une classe. Il est appelé chaque fois qu'un objet de la classe est construit ou 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 à acause 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 (), il fonctionne très bien car il n'a pas de __init__fonction ici.

bhatnaushad
la source
passer un objet à un objet de classe initialisé. Donc un objet appelable?
Ciasto piekarz
3

__call__permet de renvoyer des valeurs arbitraires, tout en __init__étant un constructeur retourne implicitement l'instance de classe. Comme d'autres réponses l'ont correctement souligné, __init__est appelé une seule fois, alors qu'il est possible d'appeler __call__plusieurs fois, au cas où l'instance initialisée est 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'
>>> 
Dmitriy Sintsov
la source
3

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

Remarque : le scénario 1 et le scénario 2 semblent identiques en termes de sortie de résultats. Mais dans le scénario 1, nous créons à nouveau une autre instance1 nouvelle instance . Dans le scénario 2, nous modifions simplement l' instance1 déjà créée . __call__est bénéfique ici car le système n'a pas besoin de créer de 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;
        }
    }
}
Uddhav Gautam
la source
3
La comparaison avec Java est complètement hors de portée de la question. Dans votre exemple, vous ne voyez aucune différence car il a été mal sélectionné, les chiffres sont les mêmes.
Aquiles Carattino
2

Nous pouvons utiliser la méthode d' appel pour utiliser d'autres méthodes de classe comme 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")  
Abhishek Jain
la source