Belajar dengan membaca secara berurutan

Multiple Inheritance dan MRO — Mewarisi dari Lebih dari Satu Induk

Pelajari multiple inheritance di Python. Gabungkan beberapa induk dengan class Anak(A, B):, lihat bagaimana method resolution order (MRO) memilih di antara method bernama sama, dan periksa urutannya dengan __mro__.

Sebelumnya kita membahas pewarisan tunggal, override, dan super(). Python mengizinkan kamu mewarisi dari beberapa induk sekaligus — itu adalah multiple inheritance. Kali ini kita bahas sintaksnya dan aturan yang menentukan induk mana yang dipanggil ketika beberapa punya nama method yang sama: method resolution order (MRO).

Sintaks dasar

Sintaksnya sederhana — daftarkan class induk dipisah koma. Menulis class Duck(Animal, Swimmer, Flyer): berarti Duck mewarisi atribut dan method dari Animal, Swimmer, dan Flyer semuanya.

Di contoh bawah, Animal membawa nama dan perilaku bicara, Swimmer menambah berenang, Flyer menambah terbang — dan Duck membungkus ketiganya menjadi satu makhluk lengkap.

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        pass


class Swimmer:
    def swim(self):
        return f"{self.name} sedang berenang"


class Flyer:
    def fly(self):
        return f"{self.name} sedang terbang"


class Duck(Animal, Swimmer, Flyer):    # multiple inheritance
    def speak(self):
        return f"{self.name} bilang kwek"


duck = Duck("Donald")
print(duck.speak())   # Donald bilang kwek
print(duck.swim())    # Donald sedang berenang
print(duck.fly())     # Donald sedang terbang
Duck mewarisi dari tiga induk
Animalname / speakSwimmerswimFlyerflyDuckspeak (override)mewarisimewarisimewarisi
Duck mewarisi atribut dan __init__ dari Animal, swim dari Swimmer, dan fly dari Flyer. Hanya speak yang di-override oleh Duck sendiri.

Bangun Duck yang membungkus tiga induk.

① Definisikan class Animal: dengan __init__(self, name) yang menetapkan self.name = name.

② Di class Swimmer:, definisikan swim(self) yang mengembalikan f"{self.name} sedang berenang". Lakukan sama untuk Flyer dengan fly(self).

③ Definisikan class Duck(Animal, Swimmer, Flyer): dengan speak(self) yang mengembalikan f"{self.name} bilang kwek".

④ Bangun duck = Duck("Donald") dan print nilai kembalian dari speak(), swim(), dan fly().

(Begitu berjalan benar, penjelasan akan muncul.)

Python Editor

Jalankan kode untuk melihat output

Method resolution order (MRO) — Induk mana yang menang?

Hal jadi rumit ketika beberapa induk punya method dengan nama sama. Jika Swimmer dan Flyer keduanya mendefinisikan method move, mana yang berjalan ketika kamu memanggil move pada instance Duck?

Python memakai method resolution order (MRO) — urutan pencarian yang tetap — untuk menelusuri class dari atas ke bawah dan menjalankan match pertama. Untuk multiple inheritance, urutannya adalah kiri ke kanan seperti ditulis di class Duck(A, B, C):. Jadi method bernama sama dari A menang; jika tidak ada, dicoba B, lalu C.

class Swimmer:
    def move(self):
        return "berenang"

class Flyer:
    def move(self):
        return "terbang"

class Duck(Swimmer, Flyer):    # kiri menang = Swimmer.move
    pass

class Goose(Flyer, Swimmer):   # balik urutan = hasil berbeda
    pass

print(Duck().move())   # berenang
print(Goose().move())  # terbang
MRO menelusuri (A, B, C) dari kiri ke kanan
Duck().move()move diDuck?tidak→ lanjutya→ jalankanmove diSwimmer?ya→ jalankan
Untuk Duck(Swimmer, Flyer), Python melihat Swimmer dulu, lalu Flyer. Jika keduanya mendefinisikan move, Swimmer.move menang. Susun ulang induk dan hasilnya berubah.

Di CPython kamu bisa membaca urutan pencarian sebenarnya dengan NamaClass.__mro__ (runtime di sini adalah MicroPython, yang tidak mengekspos atribut __mro__, jadi snippet di bawah hanya referensi untuk apa yang akan kamu lihat di Python biasa).

# Apa yang akan ditampilkan CPython
class Swimmer:
    pass
class Flyer:
    pass
class Duck(Swimmer, Flyer):
    pass

for cls in Duck.__mro__:
    print(cls.__name__)
# Duck
# Swimmer
# Flyer
# object

Apa itu object di akhir?

Setiap class di Python pada akhirnya mewarisi dari class bawaan object. Bahkan saat kamu tidak menulisnya, class Foo: secara internal adalah class Foo(object):. Itulah sebabnya __mro__ selalu berakhir dengan object.

Konfirmasi dengan eksperimen cepat bahwa urutan kamu mendaftarkan induk mengubah hasil.

① Di class Swimmer:, definisikan move(self) yang mengembalikan "berenang". Lakukan sama untuk Flyer dengan "terbang".

② Definisikan class Duck(Swimmer, Flyer): dan class Goose(Flyer, Swimmer):, keduanya hanya dengan pass.

print hasil dari Duck().move() dan Goose().move() — lihat bagaimana membalik urutan induk membalik hasil.

Python Editor

Jalankan kode untuk melihat output

Diamond inheritance dan C3 linearization

Bentuk lain yang muncul adalah diamond inheritance: B dan C masing-masing mewarisi dari A, dan D mewarisi dari keduanya B dan C — menggambar grafik pewarisan menghasilkan bentuk berlian secara harfiah.

Bentuk diamond inheritance
A(induk bersama)Bmewarisi ACmewarisi ADmewarisi B dan Cmewarisimewarisimewarisimewarisi
A adalah leluhur bersama. B dan C masing-masing mewarisi dari A. D lalu mewarisi dari kedua B dan C — anak panahnya membentuk berlian.

Python menghitung MRO dengan algoritma bernama C3 linearization, yang menghasilkan urutan cerdas «coba turunan (B, C) dulu, lalu naik ke leluhur bersama (A) di akhir».

class A:
    def method(self):
        return "A"

class B(A):
    def method(self):
        return "B"

class C(A):
    def method(self):
        return "C"

class D(B, C):       # berlian
    pass

print(D().method())  # B

Di contoh ini D().method() mengembalikan "B" karena MRO-nya D → B → C → A. Andai B tidak mendefinisikan method, method dari C yang akan jalan; tanpa itu, dari A. «Kiri dulu, tapi leluhur bersama paling akhir» — itulah cheat sheet untuk diamond inheritance.

Memanggil method induk tertentu lewat nama class

Saat kamu khusus ingin memanggil method dari induk tertentu — bukan sekadar yang menang MRO — super() hanya memberi kamu class berikutnya di MRO, jadi kamu cuma bisa memanggil paling banyak satu versi induk. Sebagai gantinya, gunakan ClassInduk.method(self, ...) untuk memilih persis yang kamu mau.

Karena panggilan lewat sisi class, kamu harus meneruskan self sendiri sebagai argumen pertama — satu-satunya keunikan yang perlu diingat.

super() vs memanggil dengan nama class
C().hello()super().hello()urutan MRO(hanya satu)A.hello(self)B.hello(self)A dan B keduanyabisa dipanggil
super() menjalankan hanya satu method (yang berikutnya di MRO). Memanggil dengan nama class seperti A.hello(self) memungkinkan kamu menunjuk induk tertentu terlepas dari MRO — berguna saat kamu mau memanggil beberapa method induk berurutan.
class A:
    def hello(self):
        print("hello dari A")

class B:
    def hello(self):
        print("hello dari B")

class C(A, B):
    def hello(self):
        A.hello(self)        # panggil hello A secara eksplisit
        B.hello(self)        # panggil hello B secara eksplisit
        print("hello dari C")

C().hello()
# hello dari A
# hello dari B
# hello dari C

Tulis class C yang memanggil hello dari kedua induk dengan nama.

① Definisikan class A: dengan hello(self) yang menjalankan print("hello dari A").

② Definisikan class B: dengan hello(self) yang menjalankan print("hello dari B").

③ Definisikan class C(A, B): dengan hello(self) yang memanggil A.hello(self)B.hello(self)print("hello dari C") dalam urutan tersebut (jangan lupa meneruskan self).

④ Jalankan C().hello() dan konfirmasi bahwa tiga baris dicetak.

Python Editor

Jalankan kode untuk melihat output

Kuat, tapi pakai dengan hemat

Multiple inheritance praktis, tetapi kamu harus menyimpan MRO di kepala terus untuk mengikuti perilakunya — itu biaya kognitif nyata. Dalam praktik, daripada memaksa multiple inheritance, memakai satu induk dan menyimpan class fitur kecil sebagai atribut (composition) sering lebih jelas. Simpan multiple inheritance untuk kasus seperti mencampur kemampuan independen seperti Swimmer / Flyer.

Yang ingin kamu lakukanCara menulisnya
Mewarisi banyak indukclass Anak(A, B): ...
Lihat induk mana yang menangNamaClass.__mro__ (CPython)
Hanya panggil yang berikut di MROsuper().method(...)
Tunjuk induk tertentuClassInduk.method(self, ...)
QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Di class Duck(Swimmer, Flyer):, jika kedua induk mendefinisikan method move, mana yang menang?

Soal 2Cara yang benar untuk membaca method resolution order (MRO) suatu class? (CPython)

Soal 3Di multiple inheritance, cara yang benar untuk memanggil method dari induk tertentu dengan nama adalah?