¿Cuál es la diferencia entre __init__ y __call__?

Respuestas:

755

El primero se usa para inicializar el objeto recién creado, y recibe los argumentos utilizados para hacer eso:

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

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

El segundo implementa el operador de llamada de función.

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

x = Foo()
x(1, 2, 3) # __call__
Cat Plus Plus
fuente
433
Por lo tanto, el __init__método se utiliza cuando la clase se llama para inicializar la instancia, mientras que el __call__método se llama cuando la instancia se llama
pratikm
1
Eso parece correcto, aparentemente las variables de instancia pueden modificarse durante la vida de una instancia, lo que puede ser beneficioso en algunos casos.
Murphy
55
Se llama a init cuando se crea una instancia de la clase: myfoo = Foo (1,4,7.8) call es una plantilla para llamar a la clase ya instanciada para hacer algo, digamos class Foo: \ def __call __ (self, zzz) Entonces, myfoo (12) llama a la clase a hacer lo que hace esa clase.
usuario1270710
1
Solo una nota: dado que las funciones en general son invocables, siempre admiten la llamada al método . Entonces puede llamar a la función también de esta manera: myFun .__ call __ (arg)
Arindam Roychowdhury
8
¿De qué es el uso práctico __call__?
mrgloom
262

La definición de un __call__()método personalizado en la metaclase permite llamar a la instancia de la clase como una función, no siempre modificando la instancia en sí.

In [1]: class A:
   ...:     def __init__(self):
   ...:         print "init"
   ...:         
   ...:     def __call__(self):
   ...:         print "call"
   ...:         
   ...:         

In [2]: a = A()
init

In [3]: a()
call
avasal
fuente
2
__call__no solo permite que una instancia se use como una función ... sino que define el cuerpo de la función que se ejecuta cuando una instancia se usa como una función.
Arthur
85

En Python, las funciones son objetos de primera clase, esto significa: las referencias de funciones pueden pasarse en entradas a otras funciones y / o métodos, y ejecutarse desde su interior.

Las instancias de clases (también conocidas como objetos) pueden tratarse como si fueran funciones: pasarlas a otros métodos / funciones y llamarlas. Para lograr esto, la __call__función de clase debe ser especializada.

def __call__(self, [args ...]) Toma como entrada un número variable de argumentos. Asumir que xes una instancia de la Clase X, x.__call__(1, 2)es análogo a la llamada x(1,2)o la instancia en sí misma como una función .

En Python, __init__()se define correctamente como Constructor de clase (así como también __del__()es el Destructor de clase). Por lo tanto, hay una distinción neta entre __init__()y __call__(): el primero crea una instancia de Class up, el segundo hace que dicha instancia sea invocable como una función sin afectar el ciclo de vida del objeto en sí (es decir __call__, no afecta el ciclo de vida de construcción / destrucción) pero puede modificar su estado interno (como se muestra a continuación).

Ejemplo.

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
fuente
2
Entiendo el concepto, pero no la característica especial de modificar su estado interno . Si en el código anterior reemplazamos def __call__simplemente con def update, le damos a la clase un updatemétodo que hace lo mismo. Ahora también puede modificar el estado interno, si se llama a continuación como s.update(7, 8). Entonces, ¿es __call__solo azúcar sintáctico?
Alex Povel
27

__call__hace que la instancia de una clase sea invocable. ¿Por qué se requeriría?

Técnicamente __init__se llama una vez __new__cuando se crea el objeto, para que pueda inicializarse.

Pero hay muchos escenarios en los que es posible que desee redefinir su objeto, decir que ha terminado con su objeto y puede encontrar la necesidad de un nuevo objeto. Con __call__usted puede redefinir el mismo objeto como si fuera nuevo.

Este es solo un caso, puede haber muchos más.

Mudit Verma
fuente
99
Para este caso específico, ¿no deberíamos simplemente crear una nueva instancia? ¿Es eficiente de alguna manera modificar y usar la misma instancia?
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
fuente
Creo que esta debería ser la respuesta aceptada. Responde con precisión.
Prasad Raghavendra
18

__init__se trataría como un constructor donde los __call__métodos se pueden llamar con objetos cualquier número de veces. Ambos __init__y las __call__funciones toman argumentos por defecto.

Vikram
fuente
1
__init__no es una función constructora pero __new__es. __init__se llama justo después__new__
dallonsi
Creo que __new__crea la instancia de la clase y recibe una clase como argumento, mientras __init__que el constructor de la instancia es la razón por la que recibe self. Una manera fácil de ver esto es en la llamada a = Foo(1,2,3)que será la función que recibirá los argumentos del constructor __init__.
Coffee_fan
13

Trataré de explicar esto usando un ejemplo, supongamos que desea imprimir un número fijo de términos de las series de Fibonacci. Recuerde que los primeros 2 términos de la serie de Fibonacci son 1s. Por ejemplo: 1, 1, 2, 3, 5, 8, 13 ...

Desea que la lista que contiene los números de Fibonacci se inicialice solo una vez y luego se actualice. Ahora podemos usar la __call__funcionalidad. Lea la respuesta de @mudit verma. Es como si quisieras que el objeto sea invocable como una función pero no reinicializado cada vez que lo llames.

P.ej:

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)

El resultado es:

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 observa que la salida __init__se llamó solo una vez, fue cuando se instancia la clase por primera vez, más tarde se llamó al objeto sin reinicializar.

Ruthvik Vaila
fuente
7

También puede usar el __call__método a favor de implementar decoradores .

Este ejemplo tomado de Python 3 Patrones, recetas y modismos

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

Salida :

ingrese la descripción de la imagen aquí

Teoman shipahi
fuente
44
¿Podría copiar la salida como texto?
Solomon Ucko
¿Cuál es el punto de este enfoque? ¿Puedes contrastarlo con un enfoque diferente?
nealmcb
7

Entonces, __init__se llama cuando está creando una instancia de cualquier clase e inicializando la variable de instancia también.

Ejemplo:

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

Y __call__se llama cuando llama al objeto como cualquier otra función.

Ejemplo:

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
fuente
1
@Redowan Delowar gracias hermano
Ayemun Hossain Ashik
4

__init__es un método especial en las clases de Python, es el método constructor de una clase. Se llama cada vez que se construye un objeto de la clase o podemos decir que inicializa un nuevo objeto. Ejemplo:

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

Si usamos A (), dará un error TypeError: __init__() missing 1 required positional argument: 'a'ya que requiere 1 argumento adebido a __init__.

........

__call__ cuando se implementa en la Clase, nos ayuda a invocar la instancia de la Clase como una llamada a la función.

Ejemplo:

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

Aquí si usamos B (), funciona bien porque no tiene una __init__función aquí.

bhatnaushad
fuente
pasar un objeto a un objeto de clase inicializado. Entonces, ¿un objeto invocable?
Ciasto piekarz
3

__call__permite devolver valores arbitrarios, mientras que __init__ser un constructor devuelve la instancia de clase implícitamente. Como otras respuestas señalaron correctamente, __init__se llama solo una vez, mientras que es posible llamar __call__varias veces, en caso de que la instancia inicializada se asigne a una variable 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'
>>> 
Dmitriy Sintsov
fuente
3

Respuestas cortas y dulces ya se proporcionan arriba. Quiero proporcionar una implementación práctica en comparación con 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 : el escenario 1 y el escenario 2 parecen iguales en términos de resultados. Pero en el escenario1, nuevamente creamos otra instancia nueva instancia1 . En el escenario 2, simplemente modificamos la instancia1 ya creada . __call__es beneficioso aquí ya que el sistema no necesita crear una nueva instancia.

Equivalente 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
fuente
3
Comparar con Java está completamente fuera del alcance de la pregunta. En su ejemplo, no ve ninguna diferencia porque se seleccionó mal, los números son los mismos.
Aquiles Carattino
2

Podemos usar el método call para usar otros métodos de clase como métodos estáticos.

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
fuente