ความแตกต่างระหว่าง __init__ กับ __call__ คืออะไร


554

ฉันต้องการทราบความแตกต่างระหว่าง__init__และ__call__วิธีการ

ตัวอย่างเช่น:

class test:

  def __init__(self):
    self.a = 10

  def __call__(self): 
    b = 20

คำตอบ:


754

ตัวแรกใช้เพื่อเริ่มต้นวัตถุที่สร้างขึ้นใหม่และรับอาร์กิวเมนต์ที่ใช้ในการทำเช่นนั้น:

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

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

ตัวที่สองใช้ตัวดำเนินการเรียกฟังก์ชัน

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

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

433
ดังนั้น__init__วิธีการที่จะใช้เมื่อชั้นถูกเรียกให้เริ่มต้นอินสแตนซ์ในขณะที่__call__วิธีการที่เรียกว่าเมื่อมีการเรียกอินสแตนซ์
pratikm

1
ดูเหมือนว่าถูกต้องตัวแปรอินสแตนซ์ที่ชัดแจ้งสามารถแก้ไขได้ในระหว่างอายุการใช้งานซึ่งอาจเป็นประโยชน์ในบางกรณี
เมอร์ฟี

5
initถูกเรียกเมื่อสร้างอินสแตนซ์ของคลาส: myfoo = Foo (1,4,7.8) การ เรียกใช้เป็นเทมเพลตสำหรับเรียกคลาสที่สร้างอินสแตนซ์แล้วให้ทำอะไรบางอย่างสมมติว่าคลาส Foo: \ def __call __ (self, zzz) จากนั้น myfoo (12) เรียกคลาสเพื่อทำสิ่งที่คลาสนั้นทำ
user1270710

1
ข้อควรทราบ: เนื่องจากฟังก์ชั่นทั่วไปเป็น callables พวกเขาจึงรองรับการเรียกใช้เมธอดเสมอ ดังนั้นคุณสามารถเรียกฟังก์ชั่นได้เช่นนี้: myFun .__ call __ (arg)
Arindam Roychowdhury

8
การใช้งานจริงของ__call__คืออะไร?
mrgloom

262

การกำหนด__call__()วิธีการที่กำหนดเองใน meta-class ทำให้อินสแตนซ์ของคลาสนั้นถูกเรียกว่าเป็นฟังก์ชั่น แต่ไม่ได้ปรับเปลี่ยนอินสแตนซ์ของตัวเองเสมอไป

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__ไม่เพียง แต่อนุญาตให้อินสแตนซ์ที่จะใช้เป็นฟังก์ชั่น ... มันกำหนดร่างกายฟังก์ชั่นที่ดำเนินการเมื่อมีการใช้อินสแตนซ์เป็นฟังก์ชั่น
อาร์เธอร์

85

ใน Python ฟังก์ชั่นเป็นวัตถุชั้นหนึ่งซึ่งหมายความว่า: การอ้างอิงฟังก์ชันสามารถส่งผ่านในอินพุตไปยังฟังก์ชันและ / หรือวิธีการอื่น ๆ และดำเนินการจากภายใน

อินสแตนซ์ของคลาส (วัตถุที่รู้จัก) สามารถปฏิบัติเหมือนกับว่าเป็นฟังก์ชัน: ส่งผ่านไปยังเมธอด / ฟังก์ชันอื่นและเรียกพวกมัน เพื่อให้บรรลุสิ่งนี้__call__ฟังก์ชันคลาสจะต้องมีความเชี่ยวชาญ

def __call__(self, [args ...]) มันจะใช้เป็นอินพุตจำนวนตัวแปรของการขัดแย้ง สมมติว่าxเป็นตัวอย่างของชั้นX, x.__call__(1, 2)จะคล้ายคลึงกับการเรียกร้องx(1,2)หรืออินสแตนซ์ตัวเองเป็นฟังก์ชั่น

ใน Python __init__()มีการกำหนด Class Constructor อย่างเหมาะสม (เช่นเดียวกับ__del__()Class Destructor) ดังนั้นจึงมีความแตกต่างสุทธิระหว่าง__init__()และ__call__(): ครั้งแรกสร้างตัวอย่างของการเรียนขึ้นที่สองทำให้เช่นอินสแตนซ์callableเป็นฟังก์ชั่นจะไม่ส่งผลกระทบต่อวงจรชีวิตของวัตถุตัวเอง (เช่น__call__ไม่ส่งผลกระทบต่อวงจรชีวิตการก่อสร้าง / ทำลาย) สามารถแก้ไขสถานะภายในของมันได้ (ดังแสดงด้านล่าง)

ตัวอย่าง.

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
ผมเข้าใจแนวคิด แต่ไม่ได้เป็นคุณสมบัติพิเศษของการปรับเปลี่ยนภายในรัฐ หากเราในรหัสข้างต้นแทนที่def __call__ด้วยdef updateเราให้วิธีการในชั้นเรียนupdateที่ทำสิ่งเดียวกัน s.update(7, 8)มันสามารถตอนนี้ยังปรับเปลี่ยนสถานะภายในถ้าเรียกว่าด้านล่าง งั้น__call__น้ำตาลซินแทคติกก็แค่นั้นใช่ไหม
Alex Povel

27

__call__ทำให้อินสแตนซ์ของคลาส callable ทำไมมันจะต้อง?

ในทางเทคนิค__init__จะถูกเรียกใช้ครั้งเดียว__new__เมื่อวัตถุถูกสร้างขึ้นเพื่อให้สามารถเริ่มต้นได้

แต่มีหลายสถานการณ์ที่คุณอาจต้องการกำหนดวัตถุของคุณใหม่บอกว่าคุณทำกับวัตถุของคุณแล้วและอาจต้องหาวัตถุใหม่ ด้วย__call__คุณสามารถกำหนดวัตถุเดียวกันเช่นถ้ามันเป็นของใหม่

นี่เป็นเพียงกรณีเดียวสามารถมีได้อีกมาก


9
สำหรับกรณีเฉพาะนี้เราไม่ควรสร้างอินสแตนซ์ใหม่หรือไม่ นี่เป็นวิธีที่มีประสิทธิภาพในการแก้ไขและใช้อินสแตนซ์เดียวกันหรือไม่
เมอร์ฟี

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

ฉันคิดว่านี่ควรเป็นคำตอบที่ยอมรับได้ มันตอบได้อย่างแม่นยำ
Prasad Raghavendra

18

__init__จะถือว่าเป็นตัวสร้างซึ่งเป็น__call__วิธีการที่สามารถเรียกกับวัตถุจำนวนครั้งใด ๆ ทั้งสองฟังก์ชั่น__init__และ__call__ใช้อาร์กิวเมนต์เริ่มต้น


1
__init__ไม่ใช่ฟังก์ชันตัวสร้าง แต่__new__เป็น __init__ถูกเรียกหลังจาก__new__
dallonsi

ผมคิดว่า__new__จะสร้างอินสแตนซ์ชั้นเรียนและได้รับชั้นเป็นอาร์กิวเมนต์ในขณะที่เป็นตัวสร้างอินสแตนซ์ซึ่งเป็นเหตุผลที่ได้รับ__init__ selfเป็นวิธีที่ง่ายที่จะเห็นนี้อยู่ในการเรียกฟังก์ชั่นที่จะได้รับข้อโต้แย้งที่สร้างจะเป็นa = Foo(1,2,3) __init__
Coffee_fan

13

ฉันจะพยายามอธิบายเรื่องนี้โดยใช้ตัวอย่างสมมติว่าคุณต้องการพิมพ์คำศัพท์จำนวนคงที่จากชุดฟีโบนักชี โปรดจำไว้ว่า 2 คำแรกของชุดฟีโบนักชีคือ 1 วินาที เช่น 1, 1, 2, 3, 5, 8, 13 ....

คุณต้องการให้รายการที่มีหมายเลขฟีโบนักชีเริ่มต้นได้เพียงครั้งเดียวและหลังจากนั้นควรอัพเดท ตอนนี้เราสามารถใช้__call__ฟังก์ชั่น อ่านคำตอบของ @mudit verma มันเหมือนกับว่าคุณต้องการให้วัตถุนั้นสามารถเรียกได้ว่าเป็นฟังก์ชั่น แต่ไม่ได้เริ่มต้นใหม่ทุกครั้งที่คุณเรียกมัน

เช่น:

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)

ผลลัพธ์คือ:

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__

หากคุณสังเกตว่าเอาต์พุต__init__ถูกเรียกเพียงครั้งเดียวนั่นคือเมื่อคลาสถูกสร้างอินสแตนซ์เป็นครั้งแรกในภายหลังบนวัตถุที่ถูกเรียกใช้โดยไม่ต้องเริ่มต้นใหม่


7

นอกจากนี้คุณยังสามารถใช้__call__วิธีการในการสนับสนุนการดำเนินการตกแต่ง

ตัวอย่างนี้นำมาจากรูปแบบ 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")

ผลผลิต :

ป้อนคำอธิบายรูปภาพที่นี่


4
คุณช่วยคัดลอกผลลัพธ์เป็นข้อความได้ไหม
โซโลมอน Ucko

จุดประสงค์ของแนวทางนี้คืออะไร? คุณสามารถเปรียบเทียบมันด้วยแนวทางที่แตกต่างได้หรือไม่?
nealmcb

7

ดังนั้น__init__ถูกเรียกเมื่อคุณสร้างอินสแตนซ์ของคลาสใด ๆ และเริ่มต้นตัวแปรอินสแตนซ์ด้วย

ตัวอย่าง:

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

และ__call__ถูกเรียกเมื่อคุณเรียกใช้ออบเจ็กต์เหมือนกับฟังก์ชันอื่น ๆ

ตัวอย่าง:

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 ขอบคุณ broh
Ayemun Hossain Ashik

4

__init__เป็นวิธีพิเศษในคลาส Python มันเป็นวิธีการสร้างสำหรับชั้นเรียน มันถูกเรียกว่าเมื่อใดก็ตามที่วัตถุของชั้นเรียนถูกสร้างขึ้นหรือเราสามารถพูดได้ว่ามันเริ่มต้นวัตถุใหม่ ตัวอย่าง:

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

ถ้าเราใช้ A () ก็จะให้ข้อผิดพลาด TypeError: __init__() missing 1 required positional argument: 'a'ตามที่ต้องการ 1 โต้แย้งเพราะa__init__

........

__call__ เมื่อมีการใช้งานใน Class ช่วยให้เราสามารถเรียกใช้อินสแตนซ์ของ Class เป็นฟังก์ชันการเรียกใช้

ตัวอย่าง:

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

ที่นี่ถ้าเราใช้ B () มันทำงานได้ดีเพราะมันไม่มี__init__ฟังก์ชั่นที่นี่


ผ่านวัตถุไปยังวัตถุชั้นเริ่มต้น ดังนั้นวัตถุ callable?
Ciasto piekarz

3

__call__อนุญาตให้ส่งคืนค่าโดยพลการในขณะที่__init__เป็นตัวสร้างส่งคืนอินสแตนซ์ของคลาสโดยปริยาย ดังที่คำตอบอื่น ๆ ถูกชี้อย่างชัดเจน__init__ถูกเรียกเพียงครั้งเดียวในขณะที่สามารถโทรได้__call__หลายครั้งในกรณีที่อินสแตนซ์เริ่มต้นถูกกำหนดให้กับตัวแปรกลาง

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

คำตอบสั้น ๆ และหวานจัดไว้แล้วข้างต้น ฉันต้องการให้มีการใช้งานจริงเมื่อเทียบกับ 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

หมายเหตุ : สถานการณ์ 1 และสถานการณ์ 2 ดูเหมือนกันในแง่ของผลลัพธ์ผลลัพธ์ แต่ในสถานการณ์ 1 เราสร้างอินสแตนซ์ใหม่อีกอินสแตนซ์ 1 อีกครั้ง ในสถานการณ์ 2 เราเพียงแก้ไขอินสแตนซ์ที่สร้างไว้แล้ว1 __call__มีประโยชน์ที่นี่เนื่องจากระบบไม่จำเป็นต้องสร้างอินสแตนซ์ใหม่

เทียบเท่าใน 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;
        }
    }
}

3
การเปรียบเทียบกับ Java นั้นอยู่นอกเหนือขอบเขตของคำถาม ในตัวอย่างของคุณคุณไม่เห็นความแตกต่างใด ๆ เนื่องจากมีการเลือกที่ไม่ดีตัวเลขเหมือนกัน
Aquiles Carattino

2

เราสามารถใช้วิธีการโทรเพื่อใช้วิธีการเรียนอื่น ๆ เป็นวิธีการคงที่

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")  
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.