Apa yang 'super' lakukan di Python?

564

Apa perbedaan antara:

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

dan:

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

Saya telah melihat supercukup banyak digunakan di kelas dengan hanya satu warisan. Saya bisa melihat mengapa Anda menggunakannya dalam banyak warisan tetapi tidak jelas apa keuntungan menggunakannya dalam situasi seperti ini.

pengguna25785
sumber

Jawaban:

309

Manfaat dari super()warisan tunggal minimal - sebagian besar, Anda tidak perlu membuat kode nama kelas dasar menjadi setiap metode yang menggunakan metode induknya.

Namun, hampir tidak mungkin untuk menggunakan multi-inheritance tanpa super(). Ini termasuk idiom umum seperti mixin, antarmuka, kelas abstrak, dll. Ini meluas ke kode yang kemudian meluas ke Anda. Jika seseorang kemudian ingin menulis kelas yang diperluas Childdan mixin, kode mereka tidak akan berfungsi dengan baik.

John Millikin
sumber
6
dapatkah Anda memberikan contoh dengan maksud Anda bahwa dengan "itu tidak akan berfungsi dengan baik"?
Charlie Parker
319

Apa bedanya?

SomeBaseClass.__init__(self) 

sarana untuk panggilan SomeBaseClass's __init__. sementara

super(Child, self).__init__()

berarti memanggil batasan __init__dari kelas induk yang mengikuti Childdalam Metode Resolution Order (MRO) instance.

Jika turunannya adalah subkelas Anak, mungkin ada orangtua yang berbeda yang datang berikutnya di MRO.

Dijelaskan secara sederhana

Saat Anda menulis kelas, Anda ingin kelas lain dapat menggunakannya. super()memudahkan kelas lain untuk menggunakan kelas yang Anda tulis.

Seperti kata Bob Martin, arsitektur yang baik memungkinkan Anda untuk menunda pengambilan keputusan selama mungkin.

super() dapat mengaktifkan arsitektur semacam itu.

Ketika kelas lain membuat subclass dari kelas yang Anda tulis, itu juga bisa mewarisi dari kelas lain. Dan kelas-kelas itu dapat memiliki __init__yang datang setelah ini __init__berdasarkan urutan kelas-kelas untuk resolusi metode.

Tanpa superAnda mungkin akan membuat kode-keras induk dari kelas yang Anda tulis (seperti contohnya). Ini berarti bahwa Anda tidak akan memanggil yang berikutnya __init__di MRO, dan karenanya Anda tidak akan dapat menggunakan kembali kode di dalamnya.

Jika Anda menulis kode sendiri untuk penggunaan pribadi, Anda mungkin tidak peduli dengan perbedaan ini. Tetapi jika Anda ingin orang lain menggunakan kode Anda, menggunakan superadalah satu hal yang memungkinkan fleksibilitas yang lebih besar bagi pengguna kode.

Python 2 lawan 3

Ini berfungsi di Python 2 dan 3:

super(Child, self).__init__()

Ini hanya berfungsi di Python 3:

super().__init__()

Ia bekerja tanpa argumen dengan bergerak ke atas dalam stack frame dan mendapatkan argumen pertama ke metode (biasanya selfuntuk metode instance atau clsuntuk metode kelas - tetapi bisa berupa nama lain) dan menemukan kelas (misalnya Child) dalam variabel bebas ( itu dilihat dengan nama __class__sebagai variabel penutupan bebas dalam metode ini).

Saya lebih suka menunjukkan cara penggunaan lintas-kompatibel super, tetapi jika Anda hanya menggunakan Python 3, Anda bisa menyebutnya tanpa argumen.

Tidak Langsung dengan Kompatibilitas Maju

Apa yang kamu berikan? Untuk pewarisan tunggal, contoh-contoh dari pertanyaan praktis identik dari sudut pandang analisis statis. Namun, menggunakan supermemberi Anda lapisan tipuan dengan kompatibilitas ke depan.

Kompatibilitas ke depan sangat penting bagi pengembang berpengalaman. Anda ingin kode Anda tetap bekerja dengan perubahan minimal saat Anda mengubahnya. Ketika Anda melihat riwayat revisi Anda, Anda ingin melihat dengan tepat apa yang berubah kapan.

Anda dapat memulai dengan satu warisan, tetapi jika Anda memutuskan untuk menambahkan kelas dasar lain, Anda hanya perlu mengubah baris dengan basis - jika basis mengubah dalam kelas yang Anda warisi (katakanlah mixin ditambahkan) Anda akan berubah tidak ada di kelas ini. Khususnya di Python 2, mendapatkan argumen superdan argumen metode yang benar bisa sulit. Jika Anda tahu Anda menggunakan superdengan benar dengan satu warisan, itu membuat debugging lebih sulit untuk maju.

Injeksi Ketergantungan

Orang lain dapat menggunakan kode Anda dan menyuntikkan orang tua ke dalam resolusi metode:

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__()

Katakanlah Anda menambahkan kelas lain ke objek Anda, dan ingin menyuntikkan kelas antara Foo dan Bar (untuk pengujian atau alasan lain):

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

Menggunakan un-super child gagal menyuntikkan dependensi karena anak yang Anda gunakan telah meng-hard-code metode yang akan dipanggil setelah sendiri:

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

Namun, kelas dengan anak yang menggunakan superdapat menyuntikkan ketergantungan dengan benar:

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

Mengatasi komentar

Mengapa di dunia ini ini berguna?

Python linierisasi pohon pewarisan yang rumit melalui algoritma linierisasi C3 untuk membuat Method Resolution Order (MRO).

Kami ingin metode dilihat dalam urutan itu .

Untuk metode yang didefinisikan dalam orangtua untuk menemukan yang berikutnya dalam urutan superitu, itu harus

  1. dapatkan mro dari tipe instance
  2. cari tipe yang mendefinisikan metode
  3. temukan tipe selanjutnya dengan metode
  4. ikat metode itu dan sebut dengan argumen yang diharapkan

The UnsuperChildseharusnya tidak memiliki akses ke InjectMe. Mengapa kesimpulan "Selalu hindari menggunakan super"? Apa yang kulewatkan di sini?

The UnsuperChildtidak tidak memiliki akses ke InjectMe. Ini adalah UnsuperInjectoryang memiliki akses ke InjectMe- namun tidak dapat memanggil metode kelas itu dari metode yang diwarisi darinya UnsuperChild.

Kedua kelas Anak bermaksud untuk memanggil metode dengan nama yang sama yang datang berikutnya di MRO, yang mungkin kelas lain yang tidak disadari ketika itu dibuat.

Yang tanpa superkode-keras metode induknya - dengan demikian telah membatasi perilaku metodenya, dan subkelas tidak dapat menyuntikkan fungsionalitas dalam rantai panggilan.

Satu dengan super memiliki fleksibilitas yang lebih besar. Rantai panggilan untuk metode dapat disadap dan fungsi disuntikkan.

Anda mungkin tidak memerlukan fungsionalitas itu, tetapi subkelas dari kode Anda mungkin.

Kesimpulan

Selalu gunakan superuntuk mereferensikan kelas induk alih-alih mengkodekannya.

Apa yang Anda maksudkan adalah untuk referensi kelas induk yang berikutnya, tidak secara khusus yang Anda lihat berasal dari anak.

Tidak menggunakan superdapat memberikan batasan yang tidak perlu pada pengguna kode Anda.

Aaron Hall
sumber
Di C, DI seperti ini . kode ada di sini . Jika saya menambahkan satu lagi implementasi listantarmuka, katakan doublylinkedlistmaka aplikasi lancar mengambilnya. Saya dapat membuat contoh saya lebih dapat dikonfigurasi dengan memperkenalkan config.txtdan menghubungkan implementasi pada waktu buka. Apakah ini contoh yang tepat? Jika ya, Bagaimana saya menghubungkan kode Anda? Lihat adv pertama DI di wiki. Di mana implementasi baru dapat dikonfigurasi? dalam kode Anda
overexchange
Implementasi baru dibuat melalui pewarisan, misalnya, di mana salah satu kelas "Injector" mewarisi dari InjectMekelas. Komentar bukan untuk diskusi, jadi saya sarankan Anda mendiskusikan hal ini lebih lanjut dengan orang lain dalam obrolan atau mengajukan pertanyaan baru di situs utama.
Aaron Hall
jawaban bagus! tetapi ketika menggunakan multiple inheritance, ada komplikasi dengan super () dan __init__fungsi. terutama jika tanda tangan __init__bervariasi antar kelas dalam hierarki. Saya telah menambahkan jawaban yang berfokus pada aspek ini
Aviad Rozenhek
35

Saya telah bermain sedikit dengan super(), dan telah mengakui bahwa kita dapat mengubah urutan panggilan.

Sebagai contoh, kita memiliki struktur hierarki berikutnya:

    A
   / \
  B   C
   \ /
    D

Dalam hal ini MRO dari D akan menjadi (hanya untuk Python 3):

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

Mari kita buat kelas tempat super()panggilan setelah eksekusi metode.

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

Jadi kita dapat melihat bahwa urutan resolusi sama dengan di MRO. Tetapi ketika kita memanggil super()di awal metode:

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

Kami memiliki urutan yang berbeda dengan urutan tuple MRO.

    A
   / 
  B  C
    /
    D 

Untuk bacaan tambahan, saya akan merekomendasikan jawaban berikutnya:

  1. Contoh linierisasi C3 dengan super (hierarki besar)
  2. Perubahan perilaku penting antara kelas gaya lama dan baru
  3. Kisah Di Dalam di Kelas Gaya Baru
SKhalymon
sumber
Saya tidak mengerti mengapa pesanan berubah. Bagian pertama saya mengerti bahwa DBCA karena D adalah kelas pertama, maka ketika memuat diri (B, C) akhirnya akan mencetak B, C maka hanya A karena B (A), C (A) menunjuk kembali ke diri untuk final bagian. Jika saya mengikuti pemahaman ini, maka bukankah bagian kedua harus seperti BCAD? Bisakah Anda jelaskan sedikit kepada saya?
JJson
Buruk saya, saya tidak memperhatikan bahwa setiap instance setiap kelas telah dimulai dengan super () terlebih dahulu. Lalu jika itu masalahnya, bukankah seharusnya ABCD? Saya entah bagaimana mengerti bagaimana ACBD muncul tetapi masih tidak bisa meyakinkan dan masih memiliki sedikit kebingungan. pemahaman saya adalah bahwa, d = D () disebut Kelas D (B, C) dengan 2 parameter-sendiri, karena super () diinisiasi terlebih dahulu kemudian B dipanggil bersama dengan atributnya lalu D tidak dicetak sebelum C karena Kelas D (B, C) berisi 2 parameter mandiri sehingga harus mengeksekusi parameter kedua yaitu Kelas C (A), setelah dieksekusi tidak ada lagi parameter mandiri yang akan dieksekusi
JJson
Ini akan didasarkan pada definisi mro .
SKhalymon
1
maka akan mencetak C lalu mencetak B dan akhirnya mencetak D. Apakah saya benar?
JJson
2
Sangat mudah untuk memahami yang kedua selama Anda mendapatkan yang pertama. Itu seperti tumpukan. Anda mendorong cetakan ke tumpukan dan melakukan super (), ketika selesai dengan A, ia mulai mencetak hal-hal dalam tumpukan itu, sehingga urutannya terbalik.
berikan matahari
35

Bukankah semua ini berasumsi bahwa kelas dasar adalah kelas gaya baru?

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

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

Tidak akan berfungsi di Python 2. class Aharus bergaya baru, yaitu:class A(object)

mhawke
sumber
20

Saat memanggil super()untuk menyelesaikan ke versi orangtua dari metode kelas, metode contoh, atau metode statis, kami ingin meneruskan kelas saat ini yang ruang lingkupnya kita sebagai argumen pertama, untuk menunjukkan ruang lingkup orangtua mana yang ingin kita selesaikan, dan sebagai argumen kedua objek yang menarik untuk menunjukkan ke objek mana kita mencoba menerapkan cakupan itu.

Pertimbangkan hirarki kelas A, Bdan Cdi mana masing-masing kelas adalah induk dari satu berikut, dan a, b, dan ccontoh masing-masing.

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

Menggunakan superdengan metode statis

misalnya menggunakan super()dari dalam __new__()metode

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

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

Penjelasan:

1 - meskipun biasanya untuk __new__()mengambil sebagai param pertama referensi ke kelas panggilan, itu tidak diimplementasikan dalam Python sebagai metode classmet, tetapi metode statis. Artinya, referensi ke kelas harus diberikan secara eksplisit sebagai argumen pertama ketika memanggil __new__()langsung:

# 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 - ketika memanggil super()untuk sampai ke kelas induk kita melewati kelas anak Asebagai argumen pertama, kemudian kita melewati referensi ke objek yang menarik, dalam hal ini referensi kelas yang disahkan ketika A.__new__(cls)dipanggil. Dalam sebagian besar kasus, ini juga merupakan rujukan ke kelas anak. Dalam beberapa situasi mungkin tidak, misalnya dalam kasus warisan beberapa generasi.

super(A, cls)

3 - karena sebagai aturan umum __new__()adalah metode statis, super(A, cls).__new__juga akan mengembalikan metode statis dan perlu diberikan semua argumen secara eksplisit, termasuk referensi ke objek insterest, dalam kasus ini cls.

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

4 - melakukan hal yang sama tanpa super

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

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

Menggunakan superdengan metode instan

misalnya menggunakan super()dari dalam__init__()

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

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

Penjelasan:

1- __init__adalah metode instance, yang berarti bahwa argumen pertama sebagai referensi untuk sebuah instance. Ketika dipanggil langsung dari instance, referensi dilewatkan secara implisit, yaitu Anda tidak perlu menentukannya:

# 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 - saat memanggil di super()dalam __init__()kita melewati kelas anak sebagai argumen pertama dan objek yang menarik sebagai argumen kedua, yang secara umum adalah referensi ke turunan dari kelas anak.

super(A, self)

3- Panggilan super(A, self)mengembalikan proxy yang akan menyelesaikan ruang lingkup dan menerapkannya selfseolah-olah sekarang menjadi instance dari kelas induk. Sebut saja proksi itu s. Karena __init__()merupakan metode instance, panggilan s.__init__(...)secara implisit akan memberikan referensi selfsebagai argumen pertama ke argumen orang tua __init__().

4- untuk melakukan hal yang sama tanpa superkita perlu memberikan referensi ke instance secara eksplisit ke versi induknya __init__().

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

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

Menggunakan superdengan metode kelas

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)

Penjelasan:

1- Metodemetode dapat dipanggil dari kelas secara langsung dan mengambil sebagai parameter pertama referensi ke kelas.

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

2 - saat memanggil super()metode class untuk menyelesaikan ke versi induknya, kami ingin meneruskan kelas anak saat ini sebagai argumen pertama untuk menunjukkan cakupan orangtua mana yang kami coba selesaikan, dan objek yang menarik sebagai argumen kedua untuk menunjukkan objek yang ingin kita terapkan lingkup itu, yang secara umum adalah referensi ke kelas anak itu sendiri atau salah satu dari subkelasnya.

super(B, cls_or_subcls)

3- Panggilan super(B, cls)memutuskan untuk lingkup Adan menerapkannya cls. Karena alternate_constructor()merupakan metode kelas panggilan super(B, cls).alternate_constructor(...)secara implisit akan memberikan referensi clssebagai argumen pertama ke Aversialternate_constructor()

super(B, cls).alternate_constructor()

4- untuk melakukan hal yang sama tanpa menggunakan super()Anda harus mendapatkan referensi ke versi tidak terikatA.alternate_constructor() (yaitu versi eksplisit fungsi). Melakukan hal ini tidak akan berhasil:

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)

Di atas tidak akan berfungsi karena A.alternate_constructor()metode mengambil referensi implisit Asebagai argumen pertama. Yang clsdisahkan di sini akan menjadi argumen kedua.

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)
Michael Ekoka
sumber
6

Banyak jawaban bagus, tetapi untuk pelajar visual: Pertama mari kita jelajahi dengan argumen super, dan kemudian tanpa. Contoh pohon warisan super

Bayangkan ada contoh yang jackdibuat dari kelas Jack, yang memiliki rantai pewarisan seperti yang ditunjukkan dengan warna hijau di gambar. Panggilan:

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

akan menggunakan MRO (Metode Resolusi Resolusi) dari jack(pohon warisannya dalam urutan tertentu), dan akan mulai mencari dari Jack. Mengapa seseorang dapat memberikan kelas induk? Nah jika kita mulai mencari dari instance jack, itu akan menemukan metode instance, intinya adalah menemukan metode induknya.

Jika seseorang tidak memberikan argumen ke super, itu seperti argumen pertama yang diteruskan adalah kelas self, dan argumen kedua yang dilewatkan adalah self. Ini dihitung secara otomatis untuk Anda dalam Python3.

Namun katakan kita tidak ingin menggunakan Jackmetode, alih-alih meneruskan Jack, kita bisa lewat Jenuntuk mulai mencari metode dari Jen.

Itu mencari satu lapisan pada satu waktu (lebar bukan kedalaman), misalnya jika Adamdan Suekeduanya memiliki metode yang diperlukan, yang dari Sueakan ditemukan terlebih dahulu.

Jika Caindan Suekeduanya memiliki metode yang diperlukan, Cainmetode akan dipanggil terlebih dahulu. Ini terkait dalam kode untuk:

Class Jen(Cain, Sue):

MRO dari kiri ke kanan.

run_the_race
sumber
2

beberapa jawaban yang bagus di sini, tetapi mereka tidak membahas bagaimana menggunakannya super()dalam kasus di mana kelas yang berbeda dalam hierarki memiliki tanda tangan yang berbeda ... terutama dalam kasus__init__

untuk menjawab bagian itu dan untuk dapat menggunakan secara efektif super()saya sarankan membaca jawaban saya super () dan mengubah tanda tangan metode kooperatif .

inilah solusi untuk skenario ini:

  1. kelas tingkat atas dalam hierarki Anda harus mewarisi dari kelas khusus seperti SuperObject:
  2. jika kelas dapat mengambil argumen yang berbeda, selalu sampaikan semua argumen yang Anda terima ke fungsi super sebagai argumen kata kunci, dan, selalu terima **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()

contoh penggunaan:

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)

keluaran:

E name=python age=28
C age=28
A
D name=python
B
SuperObject
Aviad Rozenhek
sumber
0
class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

Ini cukup mudah dimengerti.

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

Oke, apa yang terjadi sekarang jika Anda menggunakan super(Child,self)?

Ketika turunan Child dibuat, MRO (Method Resolution Order) -nya berada dalam urutan (Child, SomeBaseClass, objek) berdasarkan pewarisan. (anggap SomeBaseClass tidak memiliki orang tua lain kecuali objek default)

Dengan melewatkan Child, self, supermencari di MRO selfinstance, dan mengembalikan objek proxy di sebelah Child, dalam hal ini SomeBaseClass, objek ini kemudian memanggil __init__metode SomeBaseClass. Dengan kata lain, jika itu super(SomeBaseClass,self), objek proxy yang supermengembalikan akanobject

Untuk multi inheritance, MRO dapat berisi banyak kelas, jadi pada dasarnya superAnda dapat memutuskan di mana Anda ingin mulai mencari di MRO.

dorisxx
sumber
0

Pertimbangkan kode berikut:

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

Jika mengubah konstruktor Ymenjadi X.__init__, Anda akan mendapatkan:

X
Y
Q

Tetapi menggunakan super(Y, self).__init__(), Anda akan mendapatkan:

X
P
Y
Q

Dan Patau Qbahkan mungkin terlibat dari file lain yang Anda tidak tahu kapan Anda menulis Xdan Y. Jadi, pada dasarnya, Anda tidak akan tahu apa yang super(Child, self)akan referensi ketika Anda menulis class Y(X), bahkan tanda tangan Y sesederhana itu Y(X). Itu sebabnya super bisa menjadi pilihan yang lebih baik.

tsh
sumber