__İnit__ ve __call__ arasındaki fark nedir?


554

Ben arasındaki farkı bilmek istiyorum __init__ve __call__yöntemler.

Örneğin:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

Yanıtlar:


754

Birincisi, yeni oluşturulan nesneyi başlatmak için kullanılır ve bunu yapmak için kullanılan bağımsız değişkenleri alır:

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

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

İkinci fonksiyon çağrı operatörü uygular.

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

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

433
Yani, __init__ne zaman yöntem kullanılır sınıf örneğini başlatmak için denir iken __call__zaman yöntemi denir örneği olarak adlandırılır
pratikm

1
Bu doğru gözüküyor, görünüşe göre örnek değişkenleri, bazı durumlarda faydalı olabilecek bir örneğin ömrü boyunca değiştirilebilir.
Murphy

5
sınıfı başlatırken init çağrılır: myfoo = Foo (1,4,7.8) çağrısı , zaten başlatılan sınıfı bir şey yapmak için çağırmak için bir şablondur Foo: \ def __call __ (self, zzz) Diyelim ki myfoo (12) sınıfa o sınıfın ne yaptığını söyler.
user1270710

1
Sadece bir not: Genel olarak fonksiyonlar callables olduğundan, her zaman yöntem çağrısını destekler . Böylece işlevi de şu şekilde arayabilirsiniz: myFun .__ call __ (arg)
Arindam Roychowdhury

8
Pratik kullanımı nedir __call__?
mrgloom

262

__call__()Meta sınıfında özel bir yöntem tanımlamak , sınıfın örneğinin, her zaman örneğin kendisini değiştirmemesi için bir işlev olarak çağrılmasını sağlar.

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

In [2]: a = A()
init

In [3]: a()
call

2
__call__yalnızca bir örneğin bir işlev olarak kullanılmasına izin vermekle kalmaz ... bir örnek işlev olarak kullanıldığında yürütülen işlev gövdesini tanımlar.
Arthur

85

Python'da işlevler birinci sınıf nesnelerdir, yani: işlev başvuruları girdilere diğer işlevlere ve / veya yöntemlere aktarılabilir ve bunların içinden yürütülebilir.

Sınıf Örnekleri (diğer adıyla Nesneler), sanki işlevmiş gibi ele alınabilir: bunları diğer yöntemlere / işlevlere geçirin ve çağırın. Bunu başarmak için __call__sınıf fonksiyonunun uzmanlaşması gerekir.

def __call__(self, [args ...]) Girdi olarak değişken sayıda argüman alır. Varsayıldığında xSınıf bir örneği olarak X, x.__call__(1, 2)çağrı benzerdir x(1,2)veya bir fonksiyonu olarak kendini örneği .

Python'da __init__()düzgün bir şekilde Sınıf Yapıcısı olarak tanımlanır ( __del__()Sınıf Yıkıcı olduğu kadar). Bu nedenle, __init__()ve arasında net bir ayrım vardır __call__(): birincisi bir Sınıf yukarı örneği oluşturur, ikincisi bir işlevi nesnenin yaşam döngüsünü etkilemeden bir işlev olarak çağrılabilir yapar (yani __call__inşaat / yıkım yaşam döngüsünü etkilemez), dahili durumunu değiştirebilir (aşağıda gösterildiği gibi).

Misal.

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

2
Kavramı anlıyorum ama iç durumunu değiştirmenin özel özelliği değil . Yukarıdaki kodda yer aldığımızda def __call__sadece def updatesınıfa updateaynı şeyi yapan bir yöntem veriyoruz . Ayrıca, aşağıda belirtildiği takdirde dahili durumu da değiştirebilir s.update(7, 8). Peki, __call__sadece syntactix şekeri mi?
Alex Povel

27

__call__sınıf örneğini çağrılabilir yapar. Neden gerekli?

Nesne oluşturulduğunda teknik __init__olarak bir kez çağrılır __new__, böylece başlatılabilir.

Ancak, nesnenizi yeniden tanımlamak, nesnenizle işiniz bittiğini ve yeni bir nesneye ihtiyaç duyabileceğinizi söyleyebileceğiniz birçok senaryo vardır. İle __call__aynı nesneyi yeni gibi yeniden tanımlayabilirsiniz.

Bu sadece bir vaka, çok daha fazlası olabilir.


9
Bu özel durum için sadece yeni bir örnek oluşturmamalı mıyız? Bu, aynı örneği değiştirmek ve kullanmak için bir şekilde verimli midir?
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 ... 
>>> 

Bunun kabul edilen cevap olması gerektiğini düşünüyorum. Kesin cevaplar.
Prasad Raghavendra

18

__init__Oluşturucu olarak değerlendirilir, burada __call__yöntemler nesnelerle herhangi bir sayıda çağrılabilir. Hem __init__ve __call__işlevleri varsayılan argümanlar alıyoruz.


1
__init__bir yapıcı işlevi değil __new__,. __init__hemen sonra denir__new__
dallonsi

Bence __new__sınıf örneğini oluşturur ve argüman olarak bir sınıf alır, oysa __init__bu yüzden örnek yapıcısıdır self. Bunu görmenin kolay bir yolu a = Foo(1,2,3), yapıcı argümanlarını alacak fonksiyonun çağrılmasıdır __init__.
Coffee_fan

13

Bunu bir örnek kullanarak açıklamaya çalışacağım, fibonacci serisinden sabit sayıda terim yazdırmak istediğinizi varsayalım. Fibonacci serisinin ilk 2 teriminin 1s olduğunu unutmayın. Örn: 1, 1, 2, 3, 5, 8, 13 ....

Fibonacci numaralarını içeren listenin sadece bir kez başlatılmasını istiyorsunuz ve bundan sonra güncellenmelidir. Şimdi __call__işlevselliği kullanabiliriz . @Mudit verma'nın cevabını okuyun. Nesnenin bir işlev olarak çağrılabilir olmasını, ancak her çağrışınızda yeniden başlatılmasını istemediğiniz gibi.

Örneğin:

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)

Çıktı:

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__

Çıktının __init__, sınıf ilk kez başlatıldığında yalnızca bir kez çağrıldığını gözlemlerseniz , daha sonra nesne yeniden başlatılmadan çağrılıyordu.


7

Dekoratörler__call__ uygulamak lehine yöntemi de kullanabilirsiniz .

Bu örnek Python 3 Kalıpları, Tarifler ve Deyimlerden alınmıştır

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

Çıktı :

resim açıklamasını buraya girin


4
Çıktıyı metin olarak kopyalar mısınız?
Solomon Ucko

Bu yaklaşımın amacı nedir? Farklı bir yaklaşımla karşılaştırabilir misiniz?
nealmcb

7

Yani, __init__herhangi bir sınıfın örneğini oluştururken ve örnek değişkenini de başlatırken çağrılır.

Misal:

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

Ve __call__başka işlev gibi nesne çağırdığınızda denir.

Misal:

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

1
@Redowan Delowar teşekkürler kardeşim
Ayemun Hossain Ashik

4

__init__Python sınıflarında özel bir yöntemdir, bir sınıfın yapıcı yöntemidir. Sınıfın bir nesnesi her oluşturulduğunda çağrılır veya yeni bir nesneyi başlattığını söyleyebiliriz. Misal:

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

Biz A () kullanırsanız, bir hata verecektir TypeError: __init__() missing 1 required positional argument: 'a'o 1 argüman gerektirdiğinden adolayı __init__.

........

__call__ Sınıfta uygulandığında, Sınıf örneğini bir işlev çağrısı olarak çağırmamıza yardımcı olur.

Misal:

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

Burada B () kullanırsak, gayet iyi çalışır çünkü __init__burada bir işlevi yoktur.


bir nesneyi başlatılmış bir sınıf nesnesine iletme. Peki, çağrılabilir bir nesne?
Ciasto piekarz

3

__call____init__yapıcı olarak sınıf örneğini dolaylı olarak döndürürken, rasgele değerler döndürmeye izin verir . Diğer yanıtların düzgün bir şekilde belirtildiği gibi, bir __init__kez çağrılırken __call__, başlatılmış örneğin ara değişkene atanması durumunda, birden çok kez çağrılabilir .

>>> 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'
>>> 

3

Kısa ve tatlı cevaplar yukarıda verilmiştir. Java ile karşılaştırıldığında bazı pratik uygulamalar sağlamak istiyorum.

 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

Not : Senaryo 1 ve Senaryo 2 sonuç çıktısı açısından aynı görünüyor. Ancak senaryo1'de yine başka bir yeni yönetim ortamı örneği1 yaratıyoruz . Senaryo2'de, önceden oluşturulmuş bulut sunucusu1'i basitçe değiştiriyoruz . __call__sistemin yeni örnek oluşturmasına gerek olmadığı için burada faydalıdır.

Java ile eşdeğer

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;
        }
    }
}

3
Java ile karşılaştırmak tamamen sorunun kapsamı dışındadır. Örneğinizde, herhangi bir fark görmüyorsunuz çünkü kötü seçildi, sayılar aynı.
Aquiles Carattino

2

Statik yöntemler olarak diğer sınıf yöntemlerini kullanmak için call yöntemini kullanabiliriz.

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")  
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.