2.22. Pewarisan#

Pewarisan memungkinkan sebuah kelas memperluas kelas lain -- mulai dengan semua metode dan atributnya, kemudian menambahkan atau menimpa apa yang berbeda. Yang asli disebut basis (atau induk); ekstensi disebut subkelas.

A base class Shape pointing down to a subclass Square; Square overrides one method (area) and inherits the rest.

Square menggunakan kembali setiap metode pada Shape dan menimpa yang perlu dispesialisasikan.#

2.22.1. Mendefinisikan subkelas#

Kelas basis masuk dalam tanda kurung pada baris class:

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

    def describe(self):
        return self.name + " with area " + str(self.area())

    def area(self):
        return 0

class Square(Shape):
    def __init__(self, side):
        super().__init__("square")
        self.side = side

    def area(self):
        return self.side * self.side

s = Square(4)
print(s.describe())

Output:

square with area 16

Square mewarisi describe dari Shape tanpa perubahan dan menimpa area untuk mengembalikan nilai nyata. describe basis masih berfungsi karena memanggil self.area() -- yang diselesaikan ke override pada waktu berjalan.

2.22.2. super()#

super() mengembalikan proxy yang memungkinkan metode memanggil versi kelas-basis dari sebuah metode. Penggunaan paling umum adalah memanggil __init__ basis dari __init__ subkelas sehingga basis dapat menginisialisasi atributnya sendiri:

class Square(Shape):
    def __init__(self, side):
        super().__init__("square")    # run Shape.__init__
        self.side = side

Melupakan super().__init__ adalah sumber bug yang umum: atribut yang seharusnya ditetapkan basis tidak pernah ditetapkan, dan metode yang diwarisi yang bergantung padanya crash kemudian.

2.22.2.1. Kelas basis implisit#

Setiap kelas secara implisit mewarisi dari object, akar dari hierarki tipe Python. Metode seperti __repr__, __eq__, dan mekanisme di balik akses atribut semuanya berasal dari sana bahkan ketika sebuah kelas tidak mendeklarasikan basis. Menulis class Foo(object): secara eksplisit dan menulis class Foo: adalah setara dalam Python modern; bentuk terakhir adalah bentuk konvensional.

2.22.3. Kapan pewarisan membantu -- dan kapan tidak#

Gunakan pewarisan ketika satu kelas memang jenis yang lebih spesifik dari yang lain, berbagi sebagian besar perilakunya. Tes klasiknya adalah pemeriksaan "is-a": Square adalah Shape.

Ketika dua kelas kebetulan berbagi beberapa pembantu, pewarisan berlebihan. Gunakan komposisi sebagai gantinya: biarkan satu kelas menyimpan instans dari yang lain sebagai atribut dan gunakan melalui atribut tersebut. Bentuknya adalah "has-a", bukan "is-a":

class Logger:
    def log(self, msg):
        print("[log]", msg)

# inheritance: Worker IS-A Logger
class WorkerInherits(Logger):
    def run(self):
        self.log("starting")

# composition: Worker HAS-A Logger
class WorkerComposes:
    def __init__(self):
        self.logger = Logger()

    def run(self):
        self.logger.log("starting")

Keduanya berhasil. Versi komposisi biasanya lebih baik karena:

  • Menjaga antarmuka tetap kecil. WorkerComposes hanya mengekspos run dan logger. WorkerInherits juga mengekspos log -- pemanggil dapat menulis worker.log(...) secara langsung, apakah itu dimaksudkan atau tidak.

  • Memisahkan masa hidup. Logger dapat diganti, dibagikan antar pekerja, atau dikonstruksi secara berbeda per pekerja, semuanya tanpa menyentuh kelas Worker. Subkelas terikat pada basisnya.

  • Menghindari tabrakan nama metode. Dua basis yang keduanya mendefinisikan run bertabrakan ketika sebuah kelas mencoba mewarisi dari keduanya; dua atribut tidak pernah bertabrakan.

Aturan praktis yang berguna:

  • Gunakan pewarisan ketika subkelas benar-benar merupakan jenis dari basis dan setiap metode pada basis masuk akal juga pada subkelas -- Square adalah Shape; MemoryError adalah Exception.

  • Gunakan komposisi untuk segala hal lainnya -- ketika Anda hanya menginginkan perilaku dari kelas lain, bukan identitasnya. Sebagian besar kode nyata menggunakan komposisi jauh lebih banyak daripada menggunakan pewarisan.