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.