Inheritance (Pewarisan)
Memahami konsep pewarisan class dan method overriding
Apa itu Inheritance?
Inheritance (pewarisan) adalah mekanisme di mana sebuah class baru dapat mewarisi atribut dan method dari class yang sudah ada. Class yang diwarisi disebut parent class (superclass), dan class yang mewarisi disebut child class (subclass).
Keuntungan Inheritance
- Code Reusability: Menghindari duplikasi kode
- Extensibility: Mudah menambahkan fitur baru tanpa mengubah class yang ada
- Maintainability: Perubahan di parent class otomatis berlaku untuk child class
- Logical Hierarchy: Merepresentasikan hubungan "is-a" (adalah)
Konsep Dasar Inheritance
Terminologi
- Parent Class / Superclass / Base Class: Class yang diwarisi
- Child Class / Subclass / Derived Class: Class yang mewarisi
- Method Overriding: Mengubah implementasi method dari parent class
Hubungan "is-a"
Inheritance merepresentasikan hubungan "is-a":
- Mobil is-a Kendaraan
- Motor is-a Kendaraan
- Admin is-a User
- Mahasiswa is-a Person
Implementasi Inheritance
Membuat Parent Class
Pertama, buat class dasar yang akan diwarisi:
class Kendaraan:
"""Parent class untuk semua kendaraan"""
def __init__(self, merek, tahun):
self.merek = merek
self.tahun = tahun
self.odometer = 0
def deskripsi(self):
return f"{self.merek} ({self.tahun})"
def baca_odometer(self):
return f"Kendaraan ini telah berjalan sejauh {self.odometer} kilometer"
def update_odometer(self, km):
if km >= self.odometer:
self.odometer = km
print(f"Odometer diupdate ke {km} km")
else:
print("Tidak dapat menurunkan odometer!")Membuat Child Class
Buat child class yang mewarisi dari parent class menggunakan sintaks class ChildClass(ParentClass):
class Mobil(Kendaraan):
"""Child class yang mewarisi dari Kendaraan"""
def __init__(self, merek, tahun, tipe):
# Memanggil constructor parent class
super().__init__(merek, tahun)
# Atribut tambahan khusus untuk Mobil
self.tipe = tipe
self.bensin = 100
# Method tambahan khusus untuk Mobil
def isi_bensin(self, liter):
self.bensin += liter
return f"Bensin diisi {liter} liter. Total: {self.bensin} liter"super() Function
super() digunakan untuk memanggil method dari parent class. Paling sering digunakan untuk memanggil __init__ parent class agar atribut parent dapat diinisialisasi.
Menggunakan Child Class
Child class memiliki akses ke semua atribut dan method dari parent class:
# Membuat instance Mobil
mobil1 = Mobil("Toyota", 2022, "SUV")
# Menggunakan method dari parent class
print(mobil1.deskripsi()) # Toyota (2022)
mobil1.update_odometer(1500)
print(mobil1.baca_odometer()) # Kendaraan ini telah berjalan sejauh 1500 kilometer
# Menggunakan method khusus Mobil
print(mobil1.isi_bensin(20)) # Bensin diisi 20 liter. Total: 120 literMethod Overriding
Child class dapat mengganti (override) method dari parent class:
class Mobil(Kendaraan):
def __init__(self, merek, tahun, tipe):
super().__init__(merek, tahun)
self.tipe = tipe
self.bensin = 100
# Override method deskripsi
def deskripsi(self):
# Memanggil method parent dengan super()
base_desc = super().deskripsi()
# Menambahkan informasi tambahan
return f"{base_desc} - {self.tipe}"
def isi_bensin(self, liter):
self.bensin += liter
return f"Bensin diisi {liter} liter. Total: {self.bensin} liter"
# Test method overriding
mobil1 = Mobil("Toyota", 2022, "SUV")
print(mobil1.deskripsi()) # Toyota (2022) - SUVContoh Lengkap
Mari lihat contoh lengkap dengan multiple child classes:
# Parent Class
class Kendaraan:
def __init__(self, merek, tahun):
self.merek = merek
self.tahun = tahun
self.odometer = 0
def deskripsi(self):
return f"{self.merek} ({self.tahun})"
def baca_odometer(self):
return f"Kendaraan ini telah berjalan sejauh {self.odometer} kilometer"
def update_odometer(self, km):
if km >= self.odometer:
self.odometer = km
else:
print("Anda tidak dapat mengubah odometer!")
def jalan(self, km):
self.odometer += km
print(f"Kendaraan berjalan {km} km")
# Child Class 1: Mobil
class Mobil(Kendaraan):
def __init__(self, merek, tahun, tipe):
super().__init__(merek, tahun)
self.tipe = tipe
self.bensin = 100
def deskripsi(self):
base_desc = super().deskripsi()
return f"{base_desc} - {self.tipe}"
def isi_bensin(self, liter):
self.bensin += liter
return f"Bensin diisi {liter} liter. Total: {self.bensin} liter"
def jalan(self, km):
# Override dengan konsumsi bensin
super().jalan(km)
konsumsi = km / 10 # 10 km per liter
self.bensin -= konsumsi
print(f"Bensin tersisa: {self.bensin:.1f} liter")
# Child Class 2: Motor
class Motor(Kendaraan):
def __init__(self, merek, tahun, cc):
super().__init__(merek, tahun)
self.cc = cc
def deskripsi(self):
return f"{self.merek} ({self.tahun}) - {self.cc}cc"
def wheelie(self):
return f"{self.merek} melakukan wheelie!"
# Child Class 3: Truk (warisan dari Mobil)
class Truk(Mobil):
def __init__(self, merek, tahun, kapasitas):
# Truk adalah SUV dalam konteks ini
super().__init__(merek, tahun, "Truck")
self.kapasitas = kapasitas
self.muatan = 0
def muat_barang(self, berat):
if self.muatan + berat <= self.kapasitas:
self.muatan += berat
print(f"Barang seberat {berat} kg dimuat")
print(f"Total muatan: {self.muatan}/{self.kapasitas} kg")
else:
print(f"Kapasitas tidak cukup! Maksimal {self.kapasitas} kg")
# Penggunaan
print("=== Membuat Kendaraan ===")
kendaraan1 = Kendaraan("Generic", 2020)
mobil1 = Mobil("Toyota", 2022, "SUV")
motor1 = Motor("Honda", 2021, 150)
truk1 = Truk("Isuzu", 2023, 5000)
print("\n=== Deskripsi ===")
print(kendaraan1.deskripsi())
print(mobil1.deskripsi())
print(motor1.deskripsi())
print(truk1.deskripsi())
print("\n=== Method Parent Class ===")
mobil1.update_odometer(1500)
print(mobil1.baca_odometer())
print("\n=== Method Child Class ===")
print(mobil1.isi_bensin(20))
print(motor1.wheelie())
print("\n=== Method Override ===")
mobil1.jalan(50)
motor1.jalan(30)
print("\n=== Multi-level Inheritance ===")
truk1.muat_barang(3000)
truk1.muat_barang(2500)
print(truk1.isi_bensin(50))Multiple Inheritance
Python mendukung multiple inheritance, di mana sebuah class dapat mewarisi dari beberapa parent class:
class Elektronik:
def __init__(self, daya):
self.daya = daya
def nyalakan(self):
return f"Perangkat menyala dengan daya {self.daya}W"
class Portable:
def __init__(self, berat):
self.berat = berat
def bawa(self):
return f"Membawa perangkat dengan berat {self.berat} kg"
# Multiple inheritance
class Laptop(Elektronik, Portable):
def __init__(self, merek, daya, berat):
Elektronik.__init__(self, daya)
Portable.__init__(self, berat)
self.merek = merek
def info(self):
return f"Laptop {self.merek}"
# Penggunaan
laptop1 = Laptop("Dell", 65, 2.5)
print(laptop1.info())
print(laptop1.nyalakan())
print(laptop1.bawa())Method Resolution Order (MRO)
Ketika menggunakan multiple inheritance, Python menggunakan MRO untuk menentukan urutan pencarian method. Gunakan ClassName.__mro__ atau ClassName.mro() untuk melihat urutan.
Mengecek Inheritance
Python menyediakan beberapa built-in function untuk mengecek inheritance:
class Kendaraan:
pass
class Mobil(Kendaraan):
pass
mobil1 = Mobil()
# isinstance() - cek apakah object adalah instance dari class
print(isinstance(mobil1, Mobil)) # True
print(isinstance(mobil1, Kendaraan)) # True (karena Mobil mewarisi Kendaraan)
print(isinstance(mobil1, str)) # False
# issubclass() - cek apakah class adalah subclass dari class lain
print(issubclass(Mobil, Kendaraan)) # True
print(issubclass(Kendaraan, Mobil)) # False
print(issubclass(Mobil, object)) # True (semua class mewarisi dari object)
# type() - mendapatkan tipe dari object
print(type(mobil1)) # <class '__main__.Mobil'>
print(type(mobil1) == Mobil) # True
print(type(mobil1) == Kendaraan) # Falseisinstance vs type
Gunakan isinstance() daripada type() untuk mengecek tipe object karena isinstance() memperhitungkan inheritance, sedangkan type() hanya mengecek tipe exact.
Praktik Terbaik
1. Gunakan super()
# Good - menggunakan super()
class Mobil(Kendaraan):
def __init__(self, merek, tahun, tipe):
super().__init__(merek, tahun)
self.tipe = tipe
# Avoid - memanggil parent class secara eksplisit
class Mobil(Kendaraan):
def __init__(self, merek, tahun, tipe):
Kendaraan.__init__(self, merek, tahun)
self.tipe = tipe2. Liskov Substitution Principle
Child class harus dapat menggantikan parent class tanpa merusak program:
# Good - child class extends parent
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Square(Rectangle):
def __init__(self, size):
super().__init__(size, size)
# Avoid - child class breaks parent's contract
class Square(Rectangle):
def set_width(self, width):
self.width = width
self.height = width # Breaks rectangle behavior3. Favor Composition Over Inheritance
Kadang composition (has-a) lebih baik daripada inheritance (is-a):
# Inheritance (is-a)
class Engine:
def start(self):
return "Engine started"
class Car(Engine): # Car is-a Engine? Not really!
pass
# Composition (has-a) - Better!
class Engine:
def start(self):
return "Engine started"
class Car:
def __init__(self):
self.engine = Engine() # Car has-a Engine
def start(self):
return self.engine.start()Design Patterns dengan Inheritance
Template Method Pattern
from abc import ABC, abstractmethod
class DataProcessor(ABC):
"""Template method pattern"""
def process(self):
"""Template method"""
self.read_data()
self.process_data()
self.save_data()
@abstractmethod
def read_data(self):
pass
@abstractmethod
def process_data(self):
pass
@abstractmethod
def save_data(self):
pass
class CSVProcessor(DataProcessor):
def read_data(self):
print("Reading from CSV...")
def process_data(self):
print("Processing CSV data...")
def save_data(self):
print("Saving to database...")
class JSONProcessor(DataProcessor):
def read_data(self):
print("Reading from JSON...")
def process_data(self):
print("Processing JSON data...")
def save_data(self):
print("Saving to file...")
# Penggunaan
csv_proc = CSVProcessor()
csv_proc.process()
json_proc = JSONProcessor()
json_proc.process()Latihan
-
Buat class hierarchy untuk sistem e-commerce:
Product(parent)PhysicalProduct(child dengan atribut weight, dimensions)DigitalProduct(child dengan atribut file_size, download_link)
-
Implementasikan method overriding untuk
calculate_shipping_cost()yang berbeda untuk physical dan digital product -
Tambahkan class
UserdanPremiumUserdengan method untuk menghitung diskon -
Gunakan
isinstance()untuk memberikan diskon berbeda berdasarkan tipe user
Langkah Selanjutnya
Setelah memahami inheritance, kita akan mempelajari Encapsulation untuk melindungi data dalam class dengan access modifiers.
