Python ขยายด้วย - ใช้ super () Python 3 vs Python 2


106

เดิมทีฉันอยากจะถามคำถามนี้แต่แล้วฉันก็พบว่ามันคิดไว้แล้ว ...

Googling ไปรอบ ๆ ฉันพบตัวอย่างของการขยาย configparserนี้ สิ่งต่อไปนี้ใช้ได้กับ Python 3:

$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51) 
[GCC 4.6.3] on linux2
>>> from configparser import  SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
...     def __init_(self):
...         super().__init__()
... 
>>> cfg = AmritaConfigParser()

แต่ไม่ใช่กับ Python 2:

>>> class AmritaConfigParser(SafeConfigParser):
...       def __init__(self):
...           super(SafeConfigParser).init()
... 
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: must be type, not classob

จากนั้นฉันอ่านเล็กน้อยเกี่ยวกับรูปแบบ Python New Class กับ Old Class (เช่นที่นี่และตอนนี้ฉันสงสัยว่าฉันสามารถทำได้:

class MyConfigParser(ConfigParser.ConfigParser):
      def Write(self, fp):
          """override the module's original write funcition"""
          ....
      def MyWrite(self, fp):
          """Define new function and inherit all others"""

แต่ฉันไม่ควรเรียก init? สิ่งนี้ใน Python 2 เทียบเท่าหรือไม่:

 class AmritaConfigParser(ConfigParser.SafeConfigParser):
    #def __init__(self):
    #    super().__init__() # Python3 syntax, or rather, new style class syntax ...
    #
    # is this the equivalent of the above ? 
    def __init__(self):
        ConfigParser.SafeConfigParser.__init__(self)

1
ในตัวอย่างของคุณคุณไม่จำเป็นต้องกำหนด__init__()ในคลาสย่อยหากทั้งหมดเรียกว่า super class ' __init__()(ใน Python 2 หรือ 3) - เพียงแค่ปล่อยให้ super ถูกสืบทอดแทน
— martineau

ข้อมูลอ้างอิงที่เป็นประโยชน์: amyboyle.ninja/Python-Inheritance
— nu everest

ข้อมูลอ้างอิงที่เป็นประโยชน์พร้อมลิงก์ที่แก้ไข: amyboyle.ninja/Python-Inheritance
— fearless_fool

คำตอบ:


158
  • super()(ไม่มีข้อโต้แย้ง) ถูกนำมาใช้ใน Python 3 (พร้อมด้วย__class__):

    super() -> same as super(__class__, self)
    

    นั่นจะเท่ากับ Python 2 สำหรับคลาสสไตล์ใหม่:

    super(CurrentClass, self)
    
  • สำหรับคลาสแบบเก่าคุณสามารถใช้ได้ตลอดเวลา:

     class Classname(OldStyleParent):
        def __init__(self, *args, **kwargs):
            OldStyleParent.__init__(self, *args, **kwargs)
    

9
-1. คำตอบนี้ไม่ได้ให้ความกระจ่างอะไรสำหรับฉัน ใน Python 2 super(__class__)ให้NameError: global name '__class__' is not definedและsuper(self.__class__)ผิดพลาดเช่นกัน คุณต้องระบุอินสแตนซ์เป็นอาร์กิวเมนต์ที่สองซึ่งจะแนะนำว่าคุณต้องทำsuper(self.__class__, self)แต่ที่ไม่ถูกต้อง ถ้าClass2สืบทอดจากClass1และClass1การโทรsuper(self.__class__, self).__init__(), Class1's __init__แล้วจะเรียกตัวเองเมื่อ instantiating Class2ตัวอย่างของ
— jpmc26

เพื่อชี้แจงประเด็นฉันได้รับTypeError: super() takes at least 1 argument (0 given)เมื่อพยายามโทรsuper(self.__class__)ใน Python 2 (ซึ่งไม่สมเหตุสมผล แต่มันแสดงให้เห็นว่าข้อมูลที่ขาดหายไปจากคำตอบนี้)
— jpmc26

4
@ jpmc26: ใน python2 คุณได้รับข้อผิดพลาดนี้เนื่องจากคุณพยายามโทร__init__()โดยไม่มีอาร์กิวเมนต์บน super object ที่ไม่ถูกผูก (ซึ่งคุณได้รับจากการเรียกsuper(self.__class__)ด้วยอาร์กิวเมนต์เดียว) คุณต้องมี super object ที่ถูกผูกไว้จึงควรใช้งานsuper(CurrentClass, self).__init__()ได้ อย่าใช้self.__class__เพราะจะอ้างถึงคลาสเดียวกันเสมอเมื่อเรียกผู้ปกครองและด้วยเหตุนี้จึงสร้างลูปแบบไม่มีที่สิ้นสุดหากพาเรนต์นั้นทำเช่นเดียวกัน
— mata

__class__(สมาชิก) นอกจากนี้ยังมีอยู่ในPython2
— CristiFati

3
@CristiFati นี่ไม่ได้เกี่ยวกับ__class__สมาชิก แต่เกี่ยวกับการปิดคำศัพท์ที่สร้างขึ้นโดยปริยาย__class__ซึ่งหมายถึงคลาสที่กำลังกำหนดไว้เสมอซึ่งไม่มีอยู่ใน python2
— mata

51

ในกรณีการสืบทอดเดียว (เมื่อคุณย่อยคลาสเดียวเท่านั้น) คลาสใหม่ของคุณจะสืบทอดเมธอดของคลาสฐาน __init__ซึ่งรวมถึง ดังนั้นถ้าคุณไม่ได้กำหนดในชั้นเรียนของคุณคุณจะได้รับจากฐาน

สิ่งต่างๆเริ่มซับซ้อนหากคุณแนะนำการสืบทอดหลายรายการ (คลาสย่อยมากกว่าหนึ่งคลาสในแต่ละครั้ง) เนื่องจากถ้ามีมากกว่าหนึ่งคลาสพื้นฐานคลาส__init__ของคุณจะสืบทอดคลาสแรกเท่านั้น

ในกรณีเช่นนี้คุณควรใช้จริงๆsuperถ้าทำได้ฉันจะอธิบายว่าทำไม แต่คุณไม่สามารถทำได้เสมอไป ปัญหาคือคลาสพื้นฐานทั้งหมดของคุณต้องใช้ด้วย (และคลาสพื้นฐานของพวกเขาด้วยเช่นกัน - ทั้งต้นไม้)

หากเป็นเช่นนั้นสิ่งนี้ก็จะทำงานได้อย่างถูกต้องเช่นกัน (ใน Python 3 แต่คุณสามารถทำซ้ำใน Python 2 ได้เช่นsuperกัน):

class A:
    def __init__(self):
        print('A')
        super().__init__()

class B:
    def __init__(self):
        print('B')
        super().__init__()

class C(A, B):
    pass

C()
#prints:
#A
#B

สังเกตว่าคลาสพื้นฐานทั้งสองใช้superอย่างไรแม้ว่าจะไม่มีคลาสพื้นฐานของตัวเอง

คืออะไรsuper: เรียกใช้วิธีการจากคลาสถัดไปใน MRO (ลำดับวิธีการแก้ปัญหา) MRO สำหรับCคือ: (C, A, B, object). คุณสามารถพิมพ์C.__mro__เพื่อดูได้

ดังนั้นCสืบทอด__init__จากAและsuperในการA.__init__โทรB.__init__( BตามAใน MRO)

ดังนั้นการที่คุณไม่ทำอะไรเลยCคุณก็จะโทรหาทั้งคู่ซึ่งเป็นสิ่งที่คุณต้องการ

ตอนนี้ถ้าคุณไม่ได้ใช้superคุณก็จะได้รับมรดกA.__init__(เหมือนเดิม) แต่คราวนี้ไม่มีอะไรที่จะเรียกร้องB.__init__ให้คุณ

class A:
    def __init__(self):
        print('A')

class B:
    def __init__(self):
        print('B')

class C(A, B):
    pass

C()
#prints:
#A

ในการแก้ไขคุณต้องกำหนดC.__init__:

class C(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)

ปัญหาก็คือในแผนผัง MI ที่ซับซ้อนมากขึ้น __init__เมธอดของบางคลาสอาจถูกเรียกมากกว่าหนึ่งครั้งในขณะที่ super / MRO รับประกันว่าจะถูกเรียกเพียงครั้งเดียว


11
Notice how both base classes use super even though they don't have their own base classes.พวกเขามี. ใน py3k ทุกคลาสย่อยอ็อบเจ็กต์
— akaRem

นี่คือคำตอบที่ฉันกำลังมองหา แต่ไม่รู้จะถามอย่างไร คำอธิบาย MRO นั้นดี
— dturvene

27

ในระยะสั้นพวกเขาเทียบเท่า มาดูประวัติกัน:

(1) ในตอนแรกฟังก์ชันจะมีลักษณะดังนี้

    class MySubClass(MySuperClass):
        def __init__(self):
            MySuperClass.__init__(self)

(2) เพื่อทำให้โค้ดเป็นนามธรรมมากขึ้น (และพกพาสะดวกยิ่งขึ้น) มีการคิดค้นวิธีการทั่วไปในการรับ Super-Class เช่น:

    super(<class>, <instance>)

และฟังก์ชัน init สามารถ:

    class MySubClassBetter(MySuperClass):
        def __init__(self):
            super(MySubClassBetter, self).__init__()

อย่างไรก็ตามต้องมีการส่งผ่านทั้งคลาสและอินสแตนซ์อย่างชัดเจนให้ทำลายกฎ DRY (Don't Repeat Yourself) เล็กน้อย

(3) ใน V3.0 มันฉลาดกว่า

    super()

ก็เพียงพอแล้วในกรณีส่วนใหญ่ คุณสามารถอ้างถึงhttp://www.python.org/dev/peps/pep-3135/


23

เพียงแค่มีตัวอย่างที่ง่ายและสมบูรณ์สำหรับ Python 3 ซึ่งคนส่วนใหญ่ดูเหมือนจะใช้อยู่ในขณะนี้

class MySuper(object):
    def __init__(self,a):
        self.a = a

class MySub(MySuper):
    def __init__(self,a,b):
        self.b = b
        super().__init__(a)

my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)

ให้

42
chickenman

3

การใช้งาน python3 อื่นที่เกี่ยวข้องกับการใช้คลาสบทคัดย่อกับ super () คุณควรจำไว้ว่า

super().__init__(name, 10)

มีผลเช่นเดียวกับ

Person.__init__(self, name, 10)

โปรดจำไว้ว่ามี 'ตัวตน' ซ่อนอยู่ใน super () ดังนั้นวัตถุเดียวกันจึงส่งต่อไปยังเมธอด superclass init และแอตทริบิวต์จะถูกเพิ่มให้กับวัตถุที่เรียกมัน ดังนั้นจึงsuper()ได้รับการแปล Personและถ้าคุณรวมตัวตนที่ซ่อนอยู่คุณจะได้รับโค้ดด้านบน

from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
    name = ""
    age = 0

    def __init__(self, personName, personAge):
        self.name = personName
        self.age = personAge

    @abstractmethod
    def showName(self):
        pass

    @abstractmethod
    def showAge(self):
        pass


class Man(Person):

    def __init__(self, name, height):
        self.height = height
        # Person.__init__(self, name, 10)
        super().__init__(name, 10)  # same as Person.__init__(self, name, 10)
        # basically used to call the superclass init . This is used incase you want to call subclass init
        # and then also call superclass's init.
        # Since there's a hidden self in the super's parameters, when it's is called,
        # the superclasses attributes are a part of the same object that was sent out in the super() method

    def showIdentity(self):
        return self.name, self.age, self.height

    def showName(self):
        pass

    def showAge(self):
        pass


a = Man("piyush", "179")
print(a.showIdentity())
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.