'Siêu' làm gì trong Python?


564

Sự khác biệt giữa:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

và:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

Tôi đã thấy superđược sử dụng khá nhiều trong các lớp chỉ có thừa kế duy nhất. Tôi có thể thấy lý do tại sao bạn sử dụng nó trong nhiều kế thừa nhưng không rõ những lợi thế của việc sử dụng nó trong loại tình huống này.

Câu trả lời:


309

Lợi ích của super()việc thừa kế đơn lẻ là tối thiểu - chủ yếu, bạn không phải mã hóa tên của lớp cơ sở vào mọi phương thức sử dụng các phương thức mẹ của nó.

Tuy nhiên, gần như không thể sử dụng đa kế thừa mà không có super(). Điều này bao gồm các thành ngữ phổ biến như mixins, giao diện, lớp trừu tượng, v.v ... Điều này mở rộng thành mã mà sau này mở rộng của bạn. Nếu ai đó sau đó muốn viết một lớp mở rộng Childvà mixin, mã của họ sẽ không hoạt động đúng.


6
bạn có thể cung cấp một ví dụ bằng những gì bạn muốn nói với "nó sẽ không hoạt động đúng"?
Charlie Parker

319

Có gì khác biệt?

SomeBaseClass.__init__(self) 

có nghĩa là gọi SomeBaseClass's __init__. trong khi

super(Child, self).__init__()

có nghĩa là gọi một ràng buộc __init__từ lớp cha theo sau Childtrong Lệnh giải quyết phương thức (MRO) của thể hiện.

Nếu thể hiện là một lớp con của Child, có thể có một cha mẹ khác xuất hiện tiếp theo trong MRO.

Giải thích đơn giản

Khi bạn viết một lớp, bạn muốn các lớp khác có thể sử dụng nó. super()giúp các lớp khác sử dụng lớp bạn đang viết dễ dàng hơn.

Như Bob Martin nói, một kiến ​​trúc tốt cho phép bạn hoãn đưa ra quyết định càng lâu càng tốt.

super() có thể cho phép loại kiến ​​trúc đó.

Khi một lớp khác phân lớp lớp bạn đã viết, nó cũng có thể được kế thừa từ các lớp khác. Và các lớp đó có thể có một __init__cái xuất hiện sau cái này __init__dựa trên thứ tự của các lớp để phân giải phương thức.

Nếu không, superbạn có thể sẽ viết mã cứng cho phụ huynh của lớp bạn đang viết (giống như ví dụ này). Điều này có nghĩa là bạn sẽ không gọi tiếp theo __init__trong MRO và do đó bạn sẽ không được sử dụng lại mã trong đó.

Nếu bạn đang viết mã của riêng bạn để sử dụng cá nhân, bạn có thể không quan tâm đến sự khác biệt này. Nhưng nếu bạn muốn người khác sử dụng mã của mình, sử dụng superlà một điều cho phép linh hoạt hơn đối với người dùng mã.

Python 2 so với 3

Điều này hoạt động trong Python 2 và 3:

super(Child, self).__init__()

Điều này chỉ hoạt động trong Python 3:

super().__init__()

Nó hoạt động không có đối số bằng cách di chuyển lên trong khung stack và nhận đối số đầu tiên cho phương thức (thường là selfcho một phương thức cá thể hoặc clscho một phương thức lớp - nhưng có thể là các tên khác) và tìm lớp (ví dụ Child) trong các biến miễn phí ( nó được tìm kiếm với tên __class__là một biến đóng miễn phí trong phương thức).

Tôi thích thể hiện cách sử dụng tương thích chéo super, nhưng nếu bạn chỉ sử dụng Python 3, bạn có thể gọi nó mà không có đối số.

Cảm ứng với khả năng tương thích chuyển tiếp

Nó cung cấp cho bạn những gì? Đối với thừa kế đơn, các ví dụ từ câu hỏi thực tế giống hệt nhau từ quan điểm phân tích tĩnh. Tuy nhiên, việc sử dụng supercung cấp cho bạn một lớp cảm ứng với khả năng tương thích về phía trước.

Khả năng tương thích chuyển tiếp là rất quan trọng đối với các nhà phát triển dày dạn. Bạn muốn mã của bạn tiếp tục hoạt động với những thay đổi tối thiểu khi bạn thay đổi nó. Khi bạn nhìn vào lịch sử sửa đổi của mình, bạn muốn xem chính xác những gì đã thay đổi khi nào.

Bạn có thể bắt đầu với một thừa kế duy nhất, nhưng nếu bạn quyết định thêm một lớp cơ sở khác, bạn chỉ phải thay đổi dòng với các cơ sở - nếu các cơ sở thay đổi trong một lớp mà bạn thừa kế (giả sử một mixin được thêm vào), bạn sẽ thay đổi không có gì trong lớp này Đặc biệt trong Python 2, việc đưa các đối số đến supervà các đối số phương thức đúng có thể khó khăn. Nếu bạn biết bạn đang sử dụng superchính xác với một kế thừa duy nhất, điều đó làm cho việc gỡ lỗi ít khó khăn hơn trong tương lai.

Phụ thuộc tiêm

Những người khác có thể sử dụng mã của bạn và đưa cha mẹ vào giải pháp phương pháp:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

Giả sử bạn thêm một lớp khác vào đối tượng của mình và muốn thêm một lớp giữa Foo và Bar (để thử nghiệm hoặc một số lý do khác):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

Sử dụng đứa trẻ không siêu không thể tiêm phụ thuộc vì đứa trẻ bạn đang sử dụng đã mã hóa cứng phương thức được gọi theo phương thức của chính nó:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

Tuy nhiên, lớp có đứa trẻ sử dụng supercó thể tiêm chính xác phụ thuộc:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

Phát biểu ý kiến

Tại sao trên thế giới điều này sẽ hữu ích?

Python tuyến tính hóa một cây thừa kế phức tạp thông qua thuật toán tuyến tính hóa C3 để tạo ra thứ tự độ phân giải phương thức (MRO).

Chúng tôi muốn các phương pháp được tìm kiếm theo thứ tự đó .

Đối với một phương thức được xác định trong cha mẹ để tìm phương thức tiếp theo theo thứ tự mà không có super, nó sẽ phải

  1. lấy mro từ kiểu của thể hiện
  2. tìm loại định nghĩa phương thức
  3. tìm loại tiếp theo với phương thức
  4. liên kết phương thức đó và gọi nó với các đối số dự kiến

Không UnsuperChildnên có quyền truy cập InjectMe. Tại sao không phải là kết luận "Luôn luôn tránh sử dụng super"? Tôi đang thiếu gì ở đây?

Các UnsuperChildkhông không có quyền truy cập vào InjectMe. Đó là UnsuperInjectorquyền truy cập InjectMe- và chưa thể gọi phương thức của lớp đó từ phương thức mà nó kế thừa từ đó UnsuperChild.

Cả hai lớp Con đều có ý định gọi một phương thức có cùng tên tiếp theo trong MRO, có thể là một lớp khác mà nó không biết khi nào nó được tạo.

Phương thức không có supermã cứng của phương thức cha mẹ của nó - do đó đã hạn chế hành vi của phương thức đó và các lớp con không thể thực hiện chức năng trong chuỗi cuộc gọi.

Một trong những super có linh hoạt hơn. Chuỗi cuộc gọi cho các phương thức có thể bị chặn và chức năng được thêm vào.

Bạn có thể không cần chức năng đó, nhưng các lớp con của mã của bạn có thể.

Phần kết luận

Luôn luôn sử dụng superđể tham chiếu lớp cha thay vì mã hóa cứng.

Những gì bạn dự định là tham chiếu lớp cha mẹ là dòng tiếp theo, không cụ thể là lớp bạn thấy đứa trẻ được thừa hưởng từ đó.

Không sử dụng supercó thể đặt các ràng buộc không cần thiết cho người dùng mã của bạn.


Trong C, DI là như thế này . đang ở đây . Nếu tôi thêm một lần thực hiện listgiao diện nữa, doublylinkedlistthì ứng dụng sẽ chọn nó một cách trơn tru. Tôi có thể làm cho ví dụ của mình có thể cấu hình hơn bằng cách giới thiệu config.txtvà thực hiện liên kết khi tải. Đây có phải là ví dụ đúng? Nếu có, làm thế nào để tôi liên quan đến mã của bạn? Xem những tiến bộ đầu tiên của DI trong wiki. Trường hợp nào thực hiện cấu hình mới? trong mã của bạn
trao đổi quá

Một triển khai mới được tạo thông qua kế thừa, ví dụ, trong đó một trong các lớp "In phun" kế thừa từ InjectMelớp. Tuy nhiên, các bình luận không dành cho thảo luận, vì vậy tôi khuyên bạn nên thảo luận thêm với những người khác trong cuộc trò chuyện hoặc đặt câu hỏi mới trên trang web chính.
Aaron Hall

câu trả lời chính xác! nhưng khi sử dụng nhiều kế thừa, có các biến chứng với super () và các __init__hàm. đặc biệt nếu chữ ký của __init__các lớp khác nhau trong hệ thống phân cấp. Tôi đã thêm một câu trả lời tập trung vào khía cạnh này
Aviad Rozenhek

35

Tôi đã chơi một chút với super(), và đã nhận ra rằng chúng ta có thể thay đổi thứ tự gọi.

Ví dụ: chúng ta có cấu trúc phân cấp tiếp theo:

    A
   / \
  B   C
   \ /
    D

Trong trường hợp này, MRO của D sẽ là (chỉ dành cho Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

Hãy tạo một lớp trong đó super()các cuộc gọi sau khi thực hiện phương thức.

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / 
  B  C
    /
    D

Vì vậy, chúng ta có thể thấy thứ tự độ phân giải giống như trong MRO. Nhưng khi chúng ta gọi super()vào đầu phương thức:

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

Chúng tôi có một thứ tự khác nó được đảo ngược một thứ tự của bộ MRO.

    A
   / 
  B  C
    /
    D 

Để đọc thêm, tôi muốn giới thiệu câu trả lời tiếp theo:

  1. Ví dụ tuyến tính hóa C3 với siêu (một hệ thống phân cấp lớn)
  2. Thay đổi hành vi quan trọng giữa các lớp phong cách cũ và mới
  3. Câu chuyện bên trong về các lớp học theo phong cách mới

Tôi không hiểu tại sao thứ tự đang thay đổi. Phần đầu tiên tôi hiểu rằng DBCA vì D là lớp đầu tiên, sau đó khi tải bản thân (B, C) cuối cùng sẽ in B, C sau đó chỉ A kể từ B (A), C (A) quay lại tự cho cuối cùng phần. Nếu tôi làm theo cách hiểu này, thì phần thứ hai có giống như BCAD không? Bạn có thể vui lòng giải thích một chút cho tôi xin vui lòng.
JJson

Thật tệ, tôi đã không nhận thấy rằng mỗi trường hợp lớp đã được bắt đầu với super () trước tiên. Sau đó, nếu đó là trường hợp, nó không nên là ABCD? Tôi bằng cách nào đó hiểu cách thức mà ACBD đến nhưng vẫn không thể thuyết phục và vẫn có một chút nhầm lẫn. Tôi hiểu rằng, d = D () được gọi là Lớp D (B, C) với 2 tham số tự, vì super () được khởi tạo trước nên B được gọi cùng với các thuộc tính của nó sau đó D không được in trước C là vì Class D (B, C) chứa 2 tham số tự, do đó, nó phải thực thi cái thứ hai là Lớp C (A), sau khi thực hiện, không có nhiều tham số tự thực hiện hơn
JJson

Nó sẽ dựa trên định nghĩa mro .
SKhalymon

1
sau đó nó sẽ in C rồi in B và cuối cùng in D. Tôi có đúng không?
JJson

2
Rất dễ hiểu cái thứ hai miễn là bạn có cái thứ nhất. Nó giống như một chồng. bạn đẩy bản in '' thành chồng và thực hiện super (), khi nó hoàn thành chữ A, nó bắt đầu in mọi thứ trong ngăn xếp đó, vì vậy thứ tự đảo ngược.
cấp mặt trời

35

Không phải tất cả những điều này đều cho rằng lớp cơ sở là lớp kiểu mới sao?

class A:
    def __init__(self):
        print("A.__init__()")

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

Sẽ không hoạt động trong Python 2. class Aphải là kiểu mới, nghĩa là:class A(object)


20

Khi gọi super()để giải quyết phiên bản cha mẹ của phương thức phân loại, phương thức cá thể hoặc phương pháp tĩnh, chúng tôi muốn vượt qua lớp hiện tại có phạm vi mà chúng tôi tham gia với tư cách là đối số đầu tiên, để chỉ ra phạm vi của cha mẹ mà chúng tôi đang cố gắng giải quyết và như một đối số thứ hai đối tượng quan tâm để chỉ ra đối tượng nào chúng ta đang cố gắng áp dụng phạm vi đó.

Xem xét một hệ thống phân cấp lớp A, BCtrong đó mỗi lớp là phụ huynh của một trong những điều sau nó, và a, bccác trường hợp tương ứng của mỗi người.

super(B, b) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to b, as if b was an instance of A

super(C, c) 
# resolves to the scope of C's parent i.e. B
# and applies that scope to c

super(B, c) 
# resolves to the scope of B's parent i.e. A 
# and applies that scope to c

Sử dụng supervới tĩnh điện

ví dụ: sử dụng super()từ bên trong __new__()phương thức

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return super(A, cls).__new__(cls, *a, **kw)

Giải trình:

1- mặc dù thông thường __new__()được coi là thông số đầu tiên của nó tham chiếu đến lớp gọi, nhưng nó không được triển khai trong Python dưới dạng phân loại, mà là tĩnh đối xứng. Đó là, một tham chiếu đến một lớp phải được thông qua rõ ràng là đối số đầu tiên khi gọi __new__()trực tiếp:

# if you defined this
class A(object):
    def __new__(cls):
        pass

# calling this would raise a TypeError due to the missing argument
A.__new__()

# whereas this would be fine
A.__new__(A)

2- khi gọi super()để đến lớp cha, chúng ta chuyển lớp con Alàm đối số đầu tiên của nó, sau đó chúng ta chuyển tham chiếu đến đối tượng quan tâm, trong trường hợp này là tham chiếu lớp được truyền khi A.__new__(cls)được gọi. Trong hầu hết các trường hợp, nó cũng là một tham chiếu đến lớp con. Trong một số trường hợp, nó có thể không, ví dụ trong trường hợp thừa kế nhiều thế hệ.

super(A, cls)

3- vì như một quy tắc chung __new__()là tĩnh, nên super(A, cls).__new__cũng sẽ trả về tĩnh và cần được cung cấp tất cả các đối số một cách rõ ràng, bao gồm cả tham chiếu đến đối tượng insterest, trong trường hợp này cls.

super(A, cls).__new__(cls, *a, **kw)

4- làm điều tương tự mà không cần super

class A(object):
    def __new__(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        return object.__new__(cls, *a, **kw)

Sử dụng supervới một phương thức ví dụ

ví dụ như sử dụng super()từ bên trong__init__()

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        super(A, self).__init__(*a, **kw)

Giải trình:

1- __init__là một phương thức cá thể, nghĩa là nó lấy tham số đầu tiên của nó làm tham chiếu đến một thể hiện. Khi được gọi trực tiếp từ thể hiện, tham chiếu được truyền hoàn toàn, đó là bạn không cần chỉ định nó:

# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...

# you create an instance
a = A()

# you call `__init__()` from that instance and it works
a.__init__()

# you can also call `__init__()` with the class and explicitly pass the instance 
A.__init__(a)

2- khi gọi super()bên trong __init__()chúng ta chuyển lớp con làm đối số thứ nhất và đối tượng quan tâm là đối số thứ hai, nói chung là tham chiếu đến một thể hiện của lớp con.

super(A, self)

3- Cuộc gọi super(A, self)trả về một proxy sẽ giải quyết phạm vi và áp dụng nó selfnhư thể bây giờ nó là một thể hiện của lớp cha. Hãy gọi proxy đó s. Vì __init__()là một phương thức cá thể, cuộc gọi s.__init__(...)sẽ ngầm chuyển một tham chiếu selflà đối số đầu tiên cho cha mẹ __init__().

4 - để làm tương tự mà không cần superchúng ta chuyển một tham chiếu đến một thể hiện rõ ràng cho phiên bản của cha mẹ __init__().

class A(object): 
    def __init__(self, *a, **kw):
        # ...
        # you make some changes here
        # ...

        object.__init__(self, *a, **kw)

Sử dụng supervới một đẳng cấp

class A(object):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        print "A.alternate_constructor called"
        return cls(*a, **kw)

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return super(B, cls).alternate_constructor(*a, **kw)

Giải trình:

1- Một lớp đối xứng có thể được gọi trực tiếp từ lớp và lấy tham số đầu tiên của nó làm tham chiếu đến lớp.

# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()

2- khi gọi super()trong một lớp đối xứng để giải quyết phiên bản của cha mẹ của nó, chúng tôi muốn chuyển lớp con hiện tại làm đối số đầu tiên để chỉ ra phạm vi của cha mẹ mà chúng tôi đang cố gắng giải quyết và đối tượng quan tâm là đối số thứ hai để chỉ ra đối tượng nào chúng ta muốn áp dụng phạm vi đó, nói chung là tham chiếu đến chính lớp con hoặc một trong các lớp con của nó.

super(B, cls_or_subcls)

3- Cuộc gọi super(B, cls)giải quyết theo phạm vi Avà áp dụng nó cho cls. Vì alternate_constructor()là một đối tượng phân loại, cuộc gọi super(B, cls).alternate_constructor(...)sẽ hoàn toàn chuyển một tham chiếu clslà đối số đầu tiên cho Aphiên bản củaalternate_constructor()

super(B, cls).alternate_constructor()

4 - để làm tương tự mà không sử dụng, super()bạn sẽ cần có một tham chiếu đến phiên bản không ràng buộc của A.alternate_constructor()(tức là phiên bản rõ ràng của chức năng). Đơn giản chỉ cần làm điều này sẽ không hoạt động:

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        return A.alternate_constructor(cls, *a, **kw)

Ở trên sẽ không hoạt động vì A.alternate_constructor()phương thức lấy tham chiếu ngầm Alàm đối số đầu tiên của nó. Việc clsđược thông qua ở đây sẽ là đối số thứ hai của nó.

class B(A):
    @classmethod
    def alternate_constructor(cls, *a, **kw):
        # ...
        # whatever you want to specialize or override here
        # ...

        print "B.alternate_constructor called"
        # first we get a reference to the unbound 
        # `A.alternate_constructor` function 
        unbound_func = A.alternate_constructor.im_func
        # now we call it and pass our own `cls` as its first argument
        return unbound_func(cls, *a, **kw)

6

Nhiều câu trả lời tuyệt vời, nhưng đối với người học trực quan: Đầu tiên hãy cho phép khám phá với các đối số đến siêu, và sau đó thì không. ví dụ cây thừa kế

Hãy tưởng tượng có một thể hiện jackđược tạo ra từ lớp Jack, người có chuỗi thừa kế như thể hiện trong màu xanh lục trong hình. Gọi điện thoại:

super(Jack, jack).method(...)

sẽ sử dụng MRO (Thứ tự phân giải phương thức) của jack(cây thừa kế của nó theo một thứ tự nhất định) và sẽ bắt đầu tìm kiếm từ đó Jack. Tại sao người ta có thể cung cấp một lớp cha? Vâng, nếu chúng ta bắt đầu tìm kiếm từ cá thể jack, nó sẽ tìm ra phương thức cá thể, toàn bộ vấn đề là tìm phương thức cha mẹ của nó.

Nếu một người không cung cấp đối số cho siêu, thì giống như đối số đầu tiên được truyền vào là lớp selfvà đối số thứ hai được truyền vào là self. Đây là những tính toán tự động cho bạn trong Python3.

Tuy nhiên, nói rằng chúng tôi không muốn sử dụng Jackphương thức, thay vì truyền vào Jack, chúng tôi có thể chuyển qua Jenđể bắt đầu tìm kiếm phương thức từ trên xuống Jen.

Nó tìm kiếm một lớp tại một thời điểm (chiều rộng không phải chiều sâu), ví dụ nếu AdamSuecả hai đều có phương thức cần thiết, lớp từ Suesẽ được tìm thấy trước.

Nếu CainSuecả hai đều có phương thức cần thiết, Cainphương thức của sẽ được gọi đầu tiên. Điều này tương ứng trong mã với:

Class Jen(Cain, Sue):

MRO là từ trái sang phải.


2

Một số câu trả lời tuyệt vời ở đây, nhưng chúng không giải quyết được cách sử dụng super()trong trường hợp các lớp khác nhau trong hệ thống phân cấp có chữ ký khác nhau ... đặc biệt là trong trường hợp__init__

để trả lời phần đó và để có thể sử dụng hiệu quả, super()tôi khuyên bạn nên đọc siêu câu trả lời của tôi và thay đổi chữ ký của các phương thức hợp tác .

Đây chỉ là giải pháp cho kịch bản này:

  1. các lớp cấp cao nhất trong hệ thống phân cấp của bạn phải kế thừa từ một lớp tùy chỉnh như SuperObject:
  2. nếu các lớp có thể có các đối số khác nhau, luôn luôn chuyển tất cả các đối số bạn nhận được cho siêu hàm dưới dạng đối số từ khóa và, luôn luôn chấp nhận **kwargs.
class SuperObject:        
    def __init__(self, **kwargs):
        print('SuperObject')
        mro = type(self).__mro__
        assert mro[-1] is object
        if mro[-2] is not SuperObject:
            raise TypeError(
                'all top-level classes in this hierarchy must inherit from SuperObject',
                'the last class in the MRO should be SuperObject',
                f'mro={[cls.__name__ for cls in mro]}'
            )

        # super().__init__ is guaranteed to be object.__init__        
        init = super().__init__
        init()

sử dụng ví dụ:

class A(SuperObject):
    def __init__(self, **kwargs):
        print("A")
        super(A, self).__init__(**kwargs)

class B(SuperObject):
    def __init__(self, **kwargs):
        print("B")
        super(B, self).__init__(**kwargs)

class C(A):
    def __init__(self, age, **kwargs):
        print("C",f"age={age}")
        super(C, self).__init__(age=age, **kwargs)

class D(B):
    def __init__(self, name, **kwargs):
        print("D", f"name={name}")
        super(D, self).__init__(name=name, **kwargs)

class E(C,D):
    def __init__(self, name, age, *args, **kwargs):
        print( "E", f"name={name}", f"age={age}")
        super(E, self).__init__(name=name, age=age, *args, **kwargs)

E(name='python', age=28)

đầu ra:

E name=python age=28
C age=28
A
D name=python
B
SuperObject

0
class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

Điều này khá dễ hiểu.

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

Ok, điều gì xảy ra bây giờ nếu bạn sử dụng super(Child,self)?

Khi một cá thể Con được tạo, MRO (Thứ tự Độ phân giải Phương thức) của nó theo thứ tự (Con, Một số đối tượng, đối tượng) dựa trên sự kế thừa. (giả sử someBaseClass không có cha mẹ khác ngoại trừ đối tượng mặc định)

Bằng cách chuyển Child, self, supertìm kiếm trong MRO của selfthể hiện và trả về đối tượng proxy bên cạnh Child, trong trường hợp này là someBaseClass, đối tượng này sau đó gọi __init__phương thức của someBaseClass. Nói cách khác, nếu đó là super(SomeBaseClass,self), đối tượng proxy supertrả về sẽ làobject

Đối với đa kế thừa, MRO có thể chứa nhiều lớp, vì vậy về cơ bản supercho phép bạn quyết định nơi bạn muốn bắt đầu tìm kiếm trong MRO.


0

Hãy xem xét các mã sau đây:

class X():
    def __init__(self):
        print("X")

class Y(X):
    def __init__(self):
        # X.__init__(self)
        super(Y, self).__init__()
        print("Y")

class P(X):
    def __init__(self):
        super(P, self).__init__()
        print("P")

class Q(Y, P):
    def __init__(self):
        super(Q, self).__init__()
        print("Q")

Q()

Nếu thay đổi hàm tạo của Ythành X.__init__, bạn sẽ nhận được:

X
Y
Q

Nhưng sử dụng super(Y, self).__init__(), bạn sẽ nhận được:

X
P
Y
Q

Phoặc Qthậm chí có thể tham gia từ một tập tin mà bạn không biết khi nào bạn viết XY. Vì vậy, về cơ bản, bạn sẽ không biết những gì super(Child, self)sẽ tham chiếu khi bạn viết class Y(X), ngay cả chữ ký của Y cũng đơn giản như vậy Y(X). Đó là lý do tại sao siêu có thể là một lựa chọn tốt hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.