การเรียกคลาสผู้ปกครอง __init__ ที่มีหลายมรดกวิธีที่ถูกต้องคืออะไร?


175

ว่าฉันมีสถานการณ์สืบทอดหลาย:

class A(object):
    # code for A here

class B(object):
    # code for B here

class C(A, B):
    def __init__(self):
        # What's the right code to write here to ensure 
        # A.__init__ and B.__init__ get called?

มีสองวิธีทั่วไปเพื่อเขียนเป็นC's __init__:

  1. (แบบเก่า) ParentClass.__init__(self)
  2. (ใหม่สไตล์) super(DerivedClass, self).__init__()

อย่างไรก็ตามในกรณีใดกรณีหนึ่งถ้าคลาสแม่ ( AและB) ไม่ปฏิบัติตามแบบแผนเดียวกันรหัสจะทำงานไม่ถูกต้อง (บางคนอาจพลาดหรือถูกเรียกหลายครั้ง)

ดังนั้นวิธีที่ถูกต้องอีกครั้งคืออะไร? มันง่ายที่จะพูดว่า "แค่ทำตามทำตามอย่างใดอย่างหนึ่ง" แต่ถ้าAหรือBจากห้องสมุดของบุคคลที่สามแล้วจะเป็นอย่างไร? มีวิธีการที่สามารถตรวจสอบให้แน่ใจว่าผู้สร้างคลาสผู้ปกครองทั้งหมดได้รับการเรียก (และในลำดับที่ถูกต้องและเพียงครั้งเดียว)?

แก้ไข: เพื่อดูสิ่งที่ฉันหมายถึงถ้าฉันทำ:

class A(object):
    def __init__(self):
        print("Entering A")
        super(A, self).__init__()
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        A.__init__(self)
        B.__init__(self)
        print("Leaving C")

จากนั้นฉันจะได้รับ:

Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C

โปรดทราบว่าBinit ถูกเรียกสองครั้ง ถ้าฉันทำ:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        super(C, self).__init__()
        print("Leaving C")

จากนั้นฉันจะได้รับ:

Entering C
Entering A
Leaving A
Leaving C

โปรดทราบว่าBinit ไม่เคยถูกเรียก ดังนั้นดูเหมือนว่าเว้นแต่ฉันจะรู้ / ควบคุมผู้เริ่มต้นของชั้นเรียนที่ฉันสืบทอดมาจาก ( AและB) ฉันไม่สามารถสร้างทางเลือกที่ปลอดภัยสำหรับชั้นเรียนที่ฉันกำลังเขียน ( C)


คำตอบ:


78

ทั้งสองวิธีทำงานได้ดี วิธีการใช้super()นำไปสู่ความยืดหยุ่นมากขึ้นสำหรับคลาสย่อย

ในวิธีการโทรโดยตรงC.__init__สามารถเรียกทั้งสองและA.__init__B.__init__

เมื่อใช้super()คลาสจะต้องได้รับการออกแบบสำหรับการทำงานร่วมกันหลายมรดกที่การCโทรsuperซึ่งจะเรียกAรหัสของซึ่งจะsuperเรียกBว่ารหัสของการเรียก ดูhttp://rhettinger.wordpress.com/2011/05/26/super-considered-supersuperเพื่อดูรายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่สามารถทำได้ด้วย

[ตอบคำถามเมื่อแก้ไขแล้ว]

ดังนั้นดูเหมือนว่าเว้นแต่ฉันจะรู้ / ควบคุมผู้เริ่มต้นของชั้นเรียนที่ฉันได้รับจาก (A และ B) ฉันไม่สามารถเลือกได้อย่างปลอดภัยสำหรับชั้นเรียนที่ฉันกำลังเขียน (C)

อ้างอิงบทความที่แสดงให้เห็นถึงวิธีการจัดการกับสถานการณ์นี้โดยการเพิ่มรอบชั้นห่อหุ้มและA Bมีตัวอย่างการออกกำลังกายในหัวข้อ "วิธีการรวมชั้นเรียนแบบไม่ร่วมมือ"

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

อีกความคิดหนึ่ง: หากคุณไม่พึงพอใจกับการเขียนฟังก์ชั่นโดยใช้การสืบทอดหลาย ๆ อย่างคุณสามารถใช้การจัดองค์ประกอบเพื่อควบคุมวิธีการที่เรียกว่าโอกาสใด


4
ไม่พวกเขาทำไม่ได้ หาก init ของ B ไม่ได้เรียกว่า super แล้ว init ของ B จะไม่ถูกเรียกถ้าเราทำตามsuper().__init__()วิธีนั้น ถ้าฉันโทรA.__init__()และB.__init__()โดยตรงจากนั้น (ถ้าเรียกว่า A และ B super) ฉันจะได้รับชื่อเรียก B หลายครั้ง
Adam Parkin

3
@ AdamParkin (เกี่ยวกับคำถามของคุณว่าแก้ไขแล้ว): หากหนึ่งในคลาสผู้ปกครองไม่ได้ถูกออกแบบมาเพื่อใช้กับsuper ()มันก็จะถูกห่อหุ้มด้วยวิธีที่เพิ่มsuper call บทความที่อ้างอิงแสดงตัวอย่างการออกกำลังกายในหัวข้อ "วิธีการรวมคลาสที่ไม่ร่วมมือ"
Raymond Hettinger

1
ยังไงก็เถอะฉันก็พลาดหัวข้อนั้นเมื่อฉันอ่านบทความ ว่าสิ่งที่ผมกำลังมองหา ขอบคุณ!
Adam Parkin

1
หากคุณกำลังเขียนงูหลาม (หวังว่า 3!) และใช้การสืบทอดประเภทใด ๆ แต่โดยเฉพาะอย่างยิ่งหลายอย่างแล้วrhettinger.wordpress.com/2011/05/26/super-considered-superควรจะต้องอ่าน
Shawn Mehan

1
การประกาศเพราะในที่สุดเราก็รู้ว่าทำไมเราถึงไม่มีรถบินเมื่อเรามั่นใจว่าเราจะมีได้ในตอนนี้
msouth

66

คำตอบสำหรับคำถามของคุณขึ้นอยู่กับสิ่งสำคัญอย่างหนึ่ง: คลาสพื้นฐานของคุณได้รับการออกแบบมาสำหรับการสืบทอดหลาย ๆ

มี 3 สถานการณ์ที่แตกต่างกัน:

  1. คลาสพื้นฐานเป็นคลาสแบบสแตนด์อะโลนที่ไม่เกี่ยวข้อง

    หากคลาสพื้นฐานของคุณเป็นเอนทิตีแยกต่างหากที่สามารถทำงานได้อย่างอิสระและไม่รู้จักกันพวกเขาจะไม่ได้รับการออกแบบมาสำหรับการสืบทอดหลาย ๆ ตัวอย่าง:

    class Foo:
        def __init__(self):
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar

    สำคัญ:สังเกตว่าไม่โทรFooหรือ! นี่คือสาเหตุที่รหัสของคุณทำงานไม่ถูกต้อง เนื่องจากวิธีสืบทอดการทำงานของไดมอนด์ในไพ ธ อนคลาสที่คลาสพื้นฐานไม่ควรเรียกใช้ เมื่อคุณได้เห็นการทำเช่นนั้นจะทำลายมรดกหลายเพราะคุณจะสิ้นสุดการเรียกร้องของชั้นอื่นมากกว่า ( คำเตือน:การหลีกเลี่ยงใน-subclasses เป็นคำแนะนำส่วนบุคคลของฉันและไม่ได้หมายความว่าตามที่ตกลงฉันทามติในชุมชนหลามบางคนชอบที่จะใช้. ในทุกระดับการพิสูจน์ว่าคุณสามารถเขียนอะแดปเตอร์ถ้าชั้นไม่ได้ทำงานตามที่ คุณคาดหวัง.)Barsuper().__init__()objectsuper().__init__()__init__object.__init__()super().__init__()objectsuper

    นี่ก็หมายความว่าคุณไม่ควรเขียนคลาสที่สืบทอดมาobjectและไม่มี__init__เมธอด ไม่ได้กำหนดวิธีการที่ทุกคนมีผลเช่นเดียวกับการโทร__init__ super().__init__()หากคลาสของคุณสืบทอดโดยตรงจากobjectนั้นตรวจสอบให้แน่ใจว่าได้เพิ่มตัวสร้างเปล่า ๆ ดังนี้:

    class Base(object):
        def __init__(self):
            pass

    อย่างไรก็ตามในสถานการณ์นี้คุณจะต้องเรียกผู้สร้างหลักแต่ละคนด้วยตนเอง มีสองวิธีในการทำสิ่งนี้:

    • ไม่มี super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              Foo.__init__(self)  # explicit calls without super
              Bar.__init__(self, bar)
    • กับ super

      class FooBar(Foo, Bar):
          def __init__(self, bar='bar'):
              super().__init__()  # this calls all constructors up to Foo
              super(Foo, self).__init__(bar)  # this calls all constructors after Foo up
                                              # to Bar

    แต่ละวิธีการทั้งสองนี้มีข้อดีและข้อเสียของตัวเอง ถ้าคุณใช้superชั้นของคุณจะให้การสนับสนุนการพึ่งพาการฉีด ในทางกลับกันมันง่ายกว่าที่จะทำผิดพลาด ตัวอย่างเช่นหากคุณเปลี่ยนลำดับFooและBar(เช่นclass FooBar(Bar, Foo)) คุณจะต้องอัปเดตการsuperโทรให้ตรงกัน โดยที่superคุณไม่ต้องกังวลเกี่ยวกับสิ่งนี้และรหัสนั้นสามารถอ่านได้มากขึ้น

  2. หนึ่งในชั้นเรียนคือมิกซ์อิน

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

    ตัวอย่าง:

    class FooMixin:
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class Bar:
        def __init__(self, bar):
            self.bar = bar
    
    class FooBar(FooMixin, Bar):
        def __init__(self, bar='bar'):
            super().__init__(bar)  # a single call is enough to invoke
                                   # all parent constructors
    
            # NOTE: `FooMixin.__init__(self, bar)` would also work, but isn't
            # recommended because we don't want to hard-code the parent class.

    รายละเอียดที่สำคัญมีดังนี้:

    • มิกซ์อินโทรsuper().__init__()และส่งผ่านข้อโต้แย้งใด ๆ ที่ได้รับ
    • คลาสย่อยสืบทอดจาก mixin ก่อน : class FooBar(FooMixin, Bar). หากคำสั่งของคลาสพื้นฐานไม่ถูกต้องตัวสร้างของ mixin จะไม่ถูกเรียกใช้
  3. คลาสพื้นฐานทั้งหมดถูกออกแบบมาสำหรับการสืบทอดแบบมีส่วนร่วม

    คลาสที่ออกแบบมาสำหรับการสืบทอดแบบมีส่วนร่วมนั้นเหมือนกับมิกซ์อิน: มันผ่านการขัดแย้งที่ไม่ได้ใช้ทั้งหมดไปยังคลาสถัดไป เหมือนเมื่อก่อนเราเพียงแค่เรียกsuper().__init__()และคอนสตรัคเตอร์หลักทั้งหมดจะถูกเรียกว่าเชน

    ตัวอย่าง:

    class CoopFoo:
        def __init__(self, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.foo = 'foo'
    
    class CoopBar:
        def __init__(self, bar, **kwargs):
            super().__init__(**kwargs)  # forwards all unused arguments
            self.bar = bar
    
    class CoopFooBar(CoopFoo, CoopBar):
        def __init__(self, bar='bar'):
            super().__init__(bar=bar)  # pass all arguments on as keyword
                                       # arguments to avoid problems with
                                       # positional arguments and the order
                                       # of the parent classes

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

    และนี่ก็เป็นข้อยกเว้นที่ผมกล่าวก่อนหน้านี้: ทั้งสองCoopFooและCoopBarสืบทอดจากแต่พวกเขายังคงโทรobject super().__init__()หากพวกเขาไม่ทำเช่นนั้นจะไม่มีมรดกที่ร่วมมือกัน

บรรทัดล่าง: การใช้งานที่ถูกต้องขึ้นอยู่กับชั้นเรียนที่คุณกำลังสืบทอด

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


2
จุดที่สองของคุณพัดฉันออกไป ฉันเคยเห็น Mixins ทางด้านขวาของซุปเปอร์คลาสจริงและคิดว่าพวกเขาค่อนข้างหลวมและอันตรายเนื่องจากคุณไม่สามารถตรวจสอบว่าคลาสที่คุณกำลังผสมอยู่นั้นมีคุณสมบัติที่คุณคาดหวังหรือไม่ ฉันไม่เคยคิดถึงการใส่นายพลsuper().__init__(*args, **kwargs)เข้าไปในมิกซ์อินและเขียนมันเสียก่อน มันสมเหตุสมผลมาก
Minix

10

วิธีการอย่างใดอย่างหนึ่ง ( "รูปแบบใหม่" หรือ "แบบเก่า") จะทำงานถ้าคุณมีการควบคุมมากกว่าซอร์สโค้ดABและ มิฉะนั้นอาจจำเป็นต้องใช้คลาสอะแดปเตอร์

สามารถเข้าถึงซอร์สโค้ดได้: ใช้สไตล์ "ใหม่" อย่างถูกต้อง

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        # Use super here, instead of explicit calls to __init__
        super(C, self).__init__()
        print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C

ที่นี่ลำดับวิธีการแก้ปัญหา (MRO) สั่งการดังต่อไปนี้:

  • C(A, B)สั่งแรกแล้วA BMRO C -> A -> B -> objectคือ
  • super(A, self).__init__()ยังคงตามห่วงโซ่ MRO ริเริ่มในการC.__init__B.__init__
  • super(B, self).__init__()ยังคงตามห่วงโซ่ MRO ริเริ่มในการC.__init__object.__init__

คุณอาจจะบอกว่ากรณีนี้ถูกออกแบบมาสำหรับมรดกหลาย

สามารถเข้าถึงซอร์สโค้ดได้: การใช้ "รูปแบบเก่า" อย่างถูกต้อง

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        # Don't use super here.
        print("<- B")

class C(A, B):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        B.__init__(self)
        print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

ที่นี่ MRO ไม่สำคัญเนื่องจากA.__init__และB.__init__ถูกเรียกอย่างชัดเจน class C(B, A):ก็จะทำงานเช่นกัน

แม้ว่ากรณีนี้จะไม่ "ออกแบบ" สำหรับการสืบทอดหลายรายการในสไตล์ใหม่เหมือนเดิม แต่การสืบทอดหลายรายการยังคงเป็นไปได้


ทีนี้จะเกิดอะไรขึ้นถ้าAและBมาจากไลบรารี่ของบุคคลที่สาม - คุณไม่สามารถควบคุมซอร์สโค้ดได้AและB ? คำตอบสั้น ๆ : คุณต้องออกแบบคลาสอะแดปเตอร์ที่ใช้การsuperโทรที่จำเป็นจากนั้นใช้คลาสที่ว่างเพื่อกำหนด MRO (ดูบทความของ Raymond Hettingersuperโดยเฉพาะอย่างยิ่งในส่วน "วิธีการรวมคลาสที่ไม่ร่วมมือ")

บุคคลที่สามพ่อแม่: Aไม่ใช้super; Bทำ

class A(object):
    def __init__(self):
        print("-> A")
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        super(B, self).__init__()
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        A.__init__(self)
        super(Adapter, self).__init__()
        print("<- C")

class C(Adapter, B):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

ชั้นเรียนAdapterดำเนินการsuperเพื่อให้Cสามารถกำหนด MRO ซึ่งเข้ามาเล่นเมื่อsuper(Adapter, self).__init__()มีการดำเนินการ

แล้วถ้าเป็นอย่างอื่นล่ะ?

ผู้ปกครองบุคคลที่สาม: Aดำเนินการsuper; Bไม่

class A(object):
    def __init__(self):
        print("-> A")
        super(A, self).__init__()
        print("<- A")

class B(object):
    def __init__(self):
        print("-> B")
        print("<- B")

class Adapter(object):
    def __init__(self):
        print("-> C")
        super(Adapter, self).__init__()
        B.__init__(self)
        print("<- C")

class C(Adapter, A):
    pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C

รูปแบบเดียวกันที่นี่ยกเว้นลำดับของการดำเนินการถูกเปลี่ยนเป็นAdapter.__init__; superโทรก่อนจากนั้นโทรออกอย่างชัดเจน โปรดสังเกตว่าแต่ละกรณีที่มีผู้ปกครองที่เป็นบุคคลที่สามต้องใช้คลาสอะแดปเตอร์ที่ไม่ซ้ำกัน

ดังนั้นดูเหมือนว่าเว้นแต่ฉันจะรู้ / ควบคุมผู้เริ่มต้นของชั้นเรียนที่ฉันสืบทอดมาจาก ( AและB) ฉันไม่สามารถสร้างทางเลือกที่ปลอดภัยสำหรับชั้นเรียนที่ฉันกำลังเขียน ( C)

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


4

ดังที่เรย์มอนด์พูดในคำตอบของเขาการโทรโดยตรงA.__init__และB.__init__ทำงานได้ดีและรหัสของคุณจะสามารถอ่านได้

อย่างไรก็ตามมันไม่ได้ใช้ลิงค์การสืบทอดระหว่างCและคลาสเหล่านั้น การใช้ลิงก์นั้นจะช่วยให้คุณมีความมั่นคงมากขึ้นและทำให้การรีแฟคเตอร์ในที่สุดง่ายขึ้นและเกิดข้อผิดพลาดน้อยลง ตัวอย่างของวิธีการทำเช่นนั้น:

class C(A, B):
    def __init__(self):
        print("entering c")
        for base_class in C.__bases__:  # (A, B)
             base_class.__init__(self)
        print("leaving c")

1
คำตอบที่ดีที่สุด พบว่าสิ่งนี้มีประโยชน์โดยเฉพาะอย่างยิ่งเนื่องจากเป็นสารป้องกันอนาคตเพิ่มเติม
Stephen Ellwood

3

บทความนี้ช่วยอธิบายเกี่ยวกับการสืบทอดหลายแบบร่วมกัน:

http://www.artima.com/weblogs/viewpost.jsp?thread=281127

มันกล่าวถึงวิธีการที่มีประโยชน์mro()ที่จะแสดงลำดับการแก้ไขวิธีการ ในตัวอย่างที่ 2 ของคุณที่คุณโทรsuperเข้าAมาการsuperโทรจะดำเนินต่อใน MRO คลาสถัดไปตามลำดับคือBนี่เป็นสาเหตุที่Bเรียกว่า init เป็นครั้งแรก

นี่คือบทความทางเทคนิคเพิ่มเติมจากเว็บไซต์ไพ ธ อนอย่างเป็นทางการ:

http://www.python.org/download/releases/2.3/mro/


2

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

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

โดยพื้นฐานแล้วไม่ว่าคลาสจะได้รับการออกแบบให้เป็นคลาสย่อยโดยใช้superหรือด้วยการโทรโดยตรงไปที่คลาสพื้นฐานเป็นคุณสมบัติที่เป็นส่วนหนึ่งของคลาส "ส่วนต่อประสานสาธารณะ" และควรมีการบันทึกไว้เช่นกัน หากคุณกำลังใช้ห้องสมุดบุคคลที่สามในลักษณะที่ผู้สร้างห้องสมุดคาดหวังและห้องสมุดมีเอกสารที่สมเหตุสมผลโดยปกติแล้วมันจะบอกคุณว่าคุณต้องทำอย่างไรเพื่อซับคลาสเฉพาะบางสิ่ง ถ้าไม่เช่นนั้นคุณจะต้องดูซอร์สโค้ดสำหรับคลาสที่คุณกำลังจัดคลาสย่อยและดูว่าอนุสัญญาการเรียกคลาสพื้นฐานของคลาสนั้นคืออะไร หากคุณรวมหลายคลาสจากห้องสมุดของบุคคลที่สามหนึ่งแห่งหรือมากกว่าในแบบที่ผู้เขียนห้องสมุดไม่คาดคิดก็อาจเป็นไปไม่ได้ที่จะเรียกใช้เมธอดของ super-class ทั้งหมด; ถ้า class A เป็นส่วนหนึ่งของลำดับชั้นที่ใช้superและคลาส B เป็นส่วนหนึ่งของลำดับชั้นที่ไม่ได้ใช้ super ดังนั้นจะไม่มีการรับประกันตัวเลือกใด ๆ คุณจะต้องคิดออกกลยุทธ์ที่เกิดขึ้นกับการทำงานในแต่ละกรณี


@ RaymondHettinger ดีแล้วคุณได้เขียนและเชื่อมโยงกับบทความที่มีความคิดบางอย่างเกี่ยวกับเรื่องนั้นในคำตอบของคุณดังนั้นฉันไม่คิดว่าฉันมีอะไรเพิ่มมากนัก :) ฉันไม่คิดว่าเป็นไปได้ที่จะปรับคลาสที่ไม่ใช้ซูเปอร์โดยทั่วไปให้เป็นลำดับชั้นขั้นสูง คุณต้องหาวิธีแก้ไขที่เหมาะสมกับคลาสเฉพาะที่เกี่ยวข้อง
Ben
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.