Qual é a diferença entre __init__ e __call__?

554

Eu quero saber a diferença entre __init__e __call__métodos.

Por exemplo:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20
sam
fonte

Respostas:

755

O primeiro é usado para inicializar o objeto recém-criado e recebe argumentos usados ​​para fazer isso:

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

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

O segundo implementa o operador de chamada de função.

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

x = Foo()
x(1, 2, 3) # __call__
Cat Plus Plus
fonte
433
Portanto, o __init__método é usado quando a classe é chamada para inicializar a instância, enquanto o __call__método é chamado quando a instância é chamada
pratikm
1
Isso parece correto, aparentemente as variáveis ​​de instância podem ser modificadas durante a vida de uma instância, o que pode ser benéfico em alguns casos.
Murphy
5
O init é chamado ao instanciar a classe: myfoo = Foo (1,4,7,8) call é um modelo para chamar a classe já instanciada para fazer algo, digamos, classe Foo: \ def __call __ (self, zzz) Então, myfoo (12) chama a classe para fazer o que essa classe faz.
user1270710
1
Apenas uma observação: como as funções em geral são chamadas, elas sempre suportam a chamada de método . Então você pode chamar função também assim: MyFun .__ chamada __ (arg)
Arindam Roychowdhury
8
Qual é o uso prático __call__?
mrgloom
262

Definir um __call__()método personalizado na meta-classe permite que a instância da classe seja chamada como uma função, nem sempre modificando a própria instância.

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

In [2]: a = A()
init

In [3]: a()
call
avasal
fonte
2
__call__não apenas permite que uma instância seja usada como uma função ... ela define o corpo da função que é executada quando uma instância é usada como uma função.
Arthur Arthur
85

No Python, funções são objetos de primeira classe, isto significa: referências de funções podem ser passadas em entradas para outras funções e / ou métodos, e executadas a partir delas.

Instâncias de classes (também conhecidas como Objetos), podem ser tratadas como se fossem funções: passe-as para outros métodos / funções e as chame. Para conseguir isso, o__call__ função de classe precisa ser especializada.

def __call__(self, [args ...]) Toma como entrada um número variável de argumentos. Supondo que xseja uma instância da Classe X, x.__call__(1, 2)é análogo a chamada x(1,2)ou a própria instância como uma função .

No Python, __init__()é definido corretamente como Construtor de Classes (assim como __del__()o Destruidor de Classes). Portanto, existe uma distinção nítida entre __init__()e __call__(): o primeiro cria uma instância de Class up, o segundo torna essa instância exigível como uma função sem afetar o ciclo de vida do próprio objeto (por exemplo,__call__ é, não construção / destruição), mas ele pode modificar seu estado interno (como mostrado abaixo).

Exemplo.

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
fonte
2
Entendo o conceito, mas não a característica especial de modificar seu estado interno . Se no código acima substituirmos def __call__simplesmente por def update, damos à classe um updatemétodo que faz a mesma coisa. Agora também pode modificar o estado interno, se chamado abaixo como s.update(7, 8). Então, é __call__apenas o açúcar syntactix?
Alex Povel 06/04
27

__call__torna instável a instância de uma classe. Por que isso seria necessário?

Tecnicamente, __init__é chamado uma vez __new__quando o objeto é criado, para que possa ser inicializado.

Mas há muitos cenários em que você pode redefinir seu objeto, dizer que terminou com ele e pode encontrar a necessidade de um novo objeto. Com__call__ você pode redefinir o mesmo objeto como se fosse novo.

Este é apenas um caso, pode haver muito mais.

Mudit Verma
fonte
9
Para este caso específico, não devemos apenas criar uma nova instância? Isso é eficiente de alguma forma para modificar e usar a mesma instância.
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
fonte
Eu acho que essa deve ser a resposta aceita. Responde com precisão.
Prasad Raghavendra
18

__init__seria tratado como Construtor, onde __call__métodos podem ser chamados com objetos inúmeras vezes. Ambas as funções __init__e __call__recebem argumentos padrão.

Vikram
fonte
1
__init__não é uma função construtora, mas __new__é. __init__é chamado logo após__new__
dallonsi 19/11/19
Eu acho que __new__cria a instância da classe e recebe uma classe como argumento, enquanto que __init__é o construtor da instância e é por isso que ele recebe self. Uma maneira fácil de ver isso é na chamada a = Foo(1,2,3)a função que receberá os argumentos do construtor __init__.
Coffee_fan 28/03
13

Vou tentar explicar isso usando um exemplo, suponha que você queira imprimir um número fixo de termos da série fibonacci. Lembre-se de que os 2 primeiros termos da série fibonacci são 1s. Por exemplo: 1, 1, 2, 3, 5, 8, 13 ....

Você deseja que a lista que contém os números de fibonacci seja inicializada apenas uma vez e depois seja atualizada. Agora podemos usar a __call__funcionalidade. Leia a resposta de @mudit verma. É como se você desejasse que o objeto pudesse ser chamado como uma função, mas não reinicializado toda vez que você o chama.

Por exemplo:

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)

A saída é:

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 você observar que a saída __init__foi chamada apenas uma vez, foi quando a classe foi instanciada pela primeira vez, mais tarde, o objeto foi chamado sem ser reinicializado.

Ruthvik Vaila
fonte
7

Você também pode usar o __call__método em favor da implementação de decoradores .

Este exemplo, retirado de Python 3 Patterns, Recipes e 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")

Saída :

insira a descrição da imagem aqui

Teoman shipahi
fonte
4
Você poderia copiar a saída como texto?
Solomon Ucko
Qual é o objetivo dessa abordagem? Você pode contrastar com uma abordagem diferente?
Nealmcb 01/03/19
7

Então, __init__é chamado quando você está criando uma instância de qualquer classe e inicializando a variável de instância também.

Exemplo:

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__é chamado quando você chama o objeto como qualquer outra função.

Exemplo:

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
fonte
1
@Redowan Delowar thanks broh
Ayemun Hossain Ashik
4

__init__é um método especial nas classes Python, é o método construtor de uma classe. É chamado sempre que um objeto da classe é construído ou podemos dizer que inicializa um novo objeto. Exemplo:

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

Se usarmos A (), ocorrerá um erro TypeError: __init__() missing 1 required positional argument: 'a', pois requer 1 argumento apor causa de __init__.

........

__call__ quando implementado na classe nos ajuda a chamar a instância de classe como uma chamada de função.

Exemplo:

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

Aqui, se usarmos B (), ele funciona muito bem porque não tem uma __init__função aqui.

bhatnaushad
fonte
passando um objeto para um objeto de classe inicializado. Então, um objeto que pode ser chamado?
Ciasto piekarz
3

__call__permite retornar valores arbitrários, enquanto __init__ser construtor retorna a instância da classe implicitamente. Como outras respostas apontaram corretamente, __init__é chamado apenas uma vez, enquanto é possível chamar __call__várias vezes, caso a instância inicializada seja atribuída à variável intermediária.

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

Respostas curtas e doces já são fornecidas acima. Eu quero fornecer alguma implementação prática em comparação com 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 : os cenários 1 e 2 parecem iguais em termos de resultado. Mas no cenário1, criamos novamente outra nova instância1 . No cenário2, simplesmente modificamos a instância1 criada já . __call__é benéfico aqui, pois o sistema não precisa criar uma nova instância.

Equivalente em 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
fonte
3
Comparar com Java está completamente fora do escopo da questão. No seu exemplo, você não vê nenhuma diferença porque foi mal selecionada, os números são os mesmos.
Aquiles Carattino
2

Podemos usar o método de chamada para usar outros métodos de classe 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
fonte