Apa perbedaan antara __init__ dan __call__?

554

Saya ingin tahu perbedaan antara __init__dan __call__metode.

Sebagai contoh:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20
sam
sumber

Jawaban:

755

Yang pertama digunakan untuk menginisialisasi objek yang baru dibuat, dan menerima argumen yang digunakan untuk melakukan itu:

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

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

Operator mengimplementasikan fungsi panggilan kedua.

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

x = Foo()
x(1, 2, 3) # __call__
Cat Plus Plus
sumber
433
Jadi, __init__metode ini digunakan ketika kelas dipanggil untuk menginisialisasi instance, sedangkan __call__metode dipanggil ketika instance dipanggil
pratikm
1
Itu kelihatannya benar, rupanya variabel instance dapat dimodifikasi selama umur instance yang dapat bermanfaat dalam beberapa kasus.
Murphy
5
init disebut ketika instantiating kelas: myfoo = Foo (1,4,7.8) panggilan adalah template untuk memanggil kelas sudah dipakai untuk melakukan Foo kelas katakanlah sesuatu mari: \ def __call __ (self, zzz) Kemudian, myfoo (12) memanggil kelas untuk melakukan apa yang dilakukan kelas itu.
user1270710
1
Hanya sebuah catatan: Karena fungsi secara umum adalah callable, mereka selalu mendukung pemanggilan metode . Jadi, Anda dapat memanggil fungsi juga seperti ini: myFun .__ call __ (arg)
Arindam Roychowdhury
8
Apa penggunaan praktis __call__?
mrgloom
262

Mendefinisikan __call__()metode kustom di kelas-meta memungkinkan instance kelas dipanggil sebagai fungsi, tidak selalu memodifikasi instance itu sendiri.

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

In [2]: a = A()
init

In [3]: a()
call
avasal
sumber
2
__call__tidak hanya memungkinkan instance digunakan sebagai fungsi ... itu mendefinisikan fungsi function yang dieksekusi ketika instance digunakan sebagai fungsi.
Arthur
85

Dalam Python, fungsi adalah objek kelas satu, ini berarti: referensi fungsi dapat dikirimkan dalam input ke fungsi dan / atau metode lain, dan dieksekusi dari dalamnya.

Instances of Classes (alias Objects), dapat diperlakukan seolah-olah mereka adalah fungsi: meneruskannya ke metode / fungsi lain dan memanggil mereka. Untuk mencapai ini, __call__fungsi kelas harus dikhususkan.

def __call__(self, [args ...]) Dibutuhkan sebagai input sejumlah argumen. Asumsix menjadi instance dari Kelas X, x.__call__(1, 2)analog dengan memanggil x(1,2)atau instance itu sendiri sebagai suatu fungsi .

Dengan Python, __init__() didefinisikan dengan benar sebagai Class Constructor (dan juga __del__()Class Destructor). Oleh karena itu, ada perbedaan bersih antara __init__()dan __call__(): yang pertama membangun instance dari Kelas atas, yang kedua membuat instance tersebut dapat dipanggil sebagai fungsi akan tanpa mempengaruhi siklus hidup objek itu sendiri (yaitu __call__tidak mempengaruhi siklus hidup konstruksi / penghancuran) tetapi itu dapat memodifikasi keadaan internalnya (seperti yang ditunjukkan di bawah).

Contoh.

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
sumber
2
Saya mengerti konsepnya, tetapi bukan fitur khusus untuk memodifikasi keadaan internal . Jika kita dalam kode di atas ganti def __call__hanya dengan def update, kita memberi kelas updatemetode yang melakukan hal yang sama. Sekarang juga dapat memodifikasi keadaan internal, jika dipanggil di bawah ini sebagai s.update(7, 8). Jadi, apakah itu __call__hanya sintaksis gula?
Alex Povel
27

__call__membuat instance dari callable kelas. Mengapa itu diperlukan?

Secara teknis __init__ disebut sekali oleh__new__ ketika objek dibuat, sehingga dapat diinisialisasi.

Tetapi ada banyak skenario di mana Anda mungkin ingin mendefinisikan kembali objek Anda, katakan Anda selesai dengan objek Anda, dan mungkin menemukan kebutuhan untuk objek baru. Dengan__call__ Anda dapat mendefinisikan kembali objek yang sama seolah-olah itu baru.

Ini hanya satu kasus, mungkin ada banyak lagi.

Mudit Verma
sumber
9
Untuk kasus khusus ini, haruskah kita tidak hanya membuat contoh baru? Apakah ini efisien dalam beberapa cara untuk memodifikasi dan menggunakan contoh yang sama.
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
sumber
Saya pikir ini harus menjadi jawaban yang diterima. Jawabannya tepat.
Prasad Raghavendra
18

__init__akan diperlakukan sebagai Konstruktor di mana sebagai __call__metode dapat dipanggil dengan objek beberapa kali. Keduanya __init__dan __call__fungsi memang mengambil argumen default.

Vikram
sumber
1
__init__bukan fungsi konstruktor tetapi __new__. __init__dipanggil tepat setelah__new__
dallonsi
Saya pikir __new__menciptakan instance kelas dan menerima kelas sebagai argumen, sedangkan __init__konstruktor instance itulah sebabnya ia menerima self. Cara mudah untuk melihatnya adalah dengan memanggil a = Foo(1,2,3)fungsi yang akan menerima argumen konstruktor __init__.
Coffee_fan
13

Saya akan mencoba menjelaskan ini menggunakan contoh, misalkan Anda ingin mencetak sejumlah syarat dari seri fibonacci. Ingat bahwa 2 syarat pertama dari seri fibonacci adalah 1d. Misalnya: 1, 1, 2, 3, 5, 8, 13 ....

Anda ingin daftar yang berisi angka-angka fibonacci diinisialisasi hanya sekali dan setelah itu harus diperbarui. Sekarang kita dapat menggunakan __call__fungsionalitas. Bacalah jawaban @mudit verma. Ini seperti Anda ingin objek dapat dipanggil sebagai fungsi tetapi tidak diinisialisasi ulang setiap kali Anda menyebutnya.

Misalnya:

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)

Outputnya adalah:

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__

Jika Anda mengamati output __init__dipanggil hanya satu kali saat itulah kelas dipakai untuk pertama kalinya, nanti objek dipanggil tanpa menginisialisasi ulang.

Ruthvik Vaila
sumber
7

Anda juga dapat menggunakan __call__metode yang mendukung penerapan dekorator .

Contoh ini diambil dari Pola, Resep, dan Idiom Python 3

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

Keluaran :

masukkan deskripsi gambar di sini

Teoman shipahi
sumber
4
Bisakah Anda menyalin output sebagai teks?
Solomon Ucko
Apa gunanya pendekatan ini? Bisakah Anda kontras dengan pendekatan yang berbeda?
nealmcb
7

Jadi, __init__dipanggil saat Anda membuat turunan dari kelas apa pun dan menginisialisasi variabel turunan juga.

Contoh:

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

Dan __call__dipanggil saat Anda memanggil objek seperti fungsi lainnya.

Contoh:

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
sumber
1
@Redowan Delowar terima kasih broh
Ayemun Hossain Ashik
4

__init__adalah metode khusus di kelas Python, itu adalah metode konstruktor untuk sebuah kelas. Disebut setiap kali objek kelas dibangun atau kita dapat mengatakan itu menginisialisasi objek baru. Contoh:

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

Jika kita menggunakan A (), itu akan memberikan kesalahan TypeError: __init__() missing 1 required positional argument: 'a'karena memerlukan 1 argumen akarena __init__.

........

__call__ ketika diimplementasikan di Kelas membantu kita memanggil instance Kelas sebagai panggilan fungsi.

Contoh:

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

Di sini jika kita menggunakan B (), itu berjalan dengan baik karena tidak memiliki __init__fungsi di sini.

bhatnaushad
sumber
meneruskan objek ke objek kelas yang diinisialisasi. Jadi objek yang bisa dipanggil?
Ciasto piekarz
3

__call__memungkinkan untuk mengembalikan nilai arbitrer, sementara __init__menjadi konstruktor mengembalikan instance kelas secara implisit. Seperti yang ditunjukkan dengan tepat oleh jawaban lain, __init__dipanggil sekali saja, sementara dimungkinkan untuk menelepon __call__berkali-kali, seandainya instance yang diinisialisasi ditugaskan ke variabel perantara.

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

Jawaban singkat dan manis sudah disediakan di atas. Saya ingin memberikan beberapa implementasi praktis dibandingkan dengan 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

Catatan : skenario 1 dan skenario 2 tampaknya sama dalam hal hasil keluaran. Namun dalam scenario1, kami kembali membuat instance instance baru lain1 . Dalam scenario2, kita cukup memodifikasi instance1 yang sudah dibuat . __call__menguntungkan di sini karena sistem tidak perlu membuat instance baru.

Setara di Jawa

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
sumber
3
Membandingkan dengan Java sepenuhnya berada di luar cakupan pertanyaan. Dalam contoh Anda, Anda tidak melihat perbedaan karena dipilih dengan buruk, angkanya sama.
Aquiles Carattino
2

Kita dapat menggunakan metode panggilan untuk menggunakan metode kelas lain sebagai metode statis.

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
sumber