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.
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.
WorkerComposeshanya mengeksposrundanlogger.WorkerInheritsjuga mengeksposlog-- pemanggil dapat menulisworker.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
runbertabrakan 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 --
SquareadalahShape;MemoryErroradalahException.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.