Mixins
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Python di Coddy. Pelajaran 38 dari 64.
Mixin adalah jenis khusus dari pewarisan berganda yang digunakan untuk "mencampurkan" fungsionalitas tambahan ke dalam class. Mixin menyediakan method tertentu tanpa menjadi class lengkap.
Berikut contoh mixin sederhana:
class JSONSerializableMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)Mari kita uraikan fungsi mixin ini:
self.__dict__- Atribut khusus ini berisi dictionary dari semua atribut object beserta nilainyajson.dumps()- Fungsi ini mengubah dictionary Python menjadi string berformat JSON- Mixin ini menambahkan fungsionalitas serialisasi JSON ke class apa pun yang mewarisinya
Sekarang, mari kita lihat cara kerjanya:
class User(JSONSerializableMixin):
def __init__(self, name, email):
self.name = name
self.email = emailMixin ini menambahkan fungsionalitas JSON ke class apa pun yang mewarisinya:
user = User("Alice", "alice@example.com")
print(user.to_json())Output:
{"name": "Alice", "email": "alice@example.com"}Inti pemahamannya: class User sekarang memiliki method to_json() tanpa mendefinisikannya secara langsung. Mixin menambahkan fungsionalitas itu!
Buat beberapa mixin untuk fungsionalitas yang berbeda:
class PrintableMixin:
def pretty_print(self):
for key, value in self.__dict__.items():
print(f"{key}: {value}")
class ComparableMixin:
def __eq__(self, other):
return self.__dict__ == other.__dict__Setiap mixin mengakses self.__dict__ untuk bekerja dengan atribut object, terlepas dari class mana yang menggunakan mixin tersebut. Inilah keunggulan mixin: mixin menyediakan fungsionalitas yang dapat digunakan kembali dan berfungsi dengan atribut class apa pun.
Gabungkan beberapa mixin dalam satu class:
class Product(JSONSerializableMixin, PrintableMixin, ComparableMixin):
def __init__(self, name, price):
self.name = name
self.price = price
product1 = Product("Laptop", 999)
product2 = Product("Laptop", 999)Gunakan semua fungsionalitas mixin:
print(product1.to_json()) # Dari JSONSerializableMixin
product1.pretty_print() # Dari PrintableMixin
print(product1 == product2) # Dari ComparableMixinOutput:
{"name": "Laptop", "price": 999}
name: Laptop
price: 999
TrueKarakteristik utama mixin:
- Tidak dimaksudkan untuk dibuat menjadi instance secara mandiri
- Menyediakan fungsionalitas khusus yang dapat digunakan kembali
- Biasanya tidak memiliki method
__init__ - Namanya sering diakhiri dengan "Mixin" atau "able"
- Dapat digabungkan dengan pewarisan berganda
- Bekerja dengan
self.__dict__atau fitur object umum lainnya agar fleksibel
Poin Penting: Mixin menyediakan cara untuk berbagi fungsionalitas di berbagai hierarki class tanpa membuat pohon pewarisan yang rumit. Mixin memungkinkan kamu untuk menambahkan kapabilitas tertentu, seperti serialisasi, perbandingan, atau pencetakan, ke class mana pun yang membutuhkannya. Hal ini mendorong penggunaan ulang kode dan membuat class tetap berfokus pada tanggung jawab utamanya.
Tantangan
SedangDalam tantangan ini, kamu akan menerapkan sistem e-commerce sederhana menggunakan mixin dan pewarisan.
Terapkan class yang diperlukan dalam file-file ini (ikuti komentar TODO di setiap file):
printablemixin.py: Buat PrintableMixin dengan fungsi output berformatdiscountmixin.py: Terapkan DiscountMixin untuk perhitungan pengurangan hargashippablemixin.py: Buat ShippableMixin dengan fitur berat dan biaya pengirimanproduct.py: Kembangkan class Product dasar dengan pewarisan mixin yang sesuaiphysicalproduct.py: Buat class PhysicalProduct yang memperluas Productdigitalproduct.py: Terapkan class DigitalProduct dengan perilaku diskon khusus
Coba sendiri
from product import Product
from physicalproduct import PhysicalProduct
from digitalproduct import DigitalProduct
from printablemixin import PrintableMixin
from discountmixin import DiscountMixin
from shippablemixin import ShippableMixin
def test_basic_functionality():
# Uji fungsi dasar semua class
p = Product("Laptop", 1000)
assert p.print_details() == "Product: Laptop, Price: $1000", f"Print details failed: {p.print_details()}"
assert p.apply_discount(10) == 900, f"Discount calculation failed: {p.apply_discount(10)}"
physical = PhysicalProduct("Desk", 500)
physical.set_weight(30)
assert physical.calculate_shipping() == 15, f"Shipping calculation failed: {physical.calculate_shipping()}"
digital = DigitalProduct("Software", 200)
assert digital.apply_discount(10) == 180, f"Digital discount failed: {digital.apply_discount(10)}"
print("Basic functionality test passed!")
def test_edge_cases():
# Uji kasus tepi seperti nilai nol dan negatif
p = Product("Free Item", 0)
assert p.apply_discount(10) == 0, f"Zero price discount failed: {p.apply_discount(10)}"
p_neg = Product("Negative Item", -100)
assert p_neg.apply_discount(10) == -90, f"Negative price discount failed: {p_neg.apply_discount(10)}"
physical = PhysicalProduct("Empty Box", 10)
physical.set_weight(0)
assert physical.calculate_shipping() == 0, f"Zero weight shipping failed: {physical.calculate_shipping()}"
physical_neg = PhysicalProduct("Anti-Gravity Item", 10)
physical_neg.set_weight(-5)
assert physical_neg.calculate_shipping() == -2.5, f"Negative weight shipping failed: {physical_neg.calculate_shipping()}"
print("Edge cases test passed!")
def test_large_values():
# Uji dengan nilai yang sangat besar
p = Product("Expensive Item", 1000000)
assert p.apply_discount(10) == 900000, f"Large value discount failed: {p.apply_discount(10)}"
physical = PhysicalProduct("Heavy Item", 500)
physical.set_weight(1000)
assert physical.calculate_shipping() == 500, f"Large weight shipping failed: {physical.calculate_shipping()}"
print("Large values test passed!")
def test_inheritance():
# Uji hubungan pewarisan
p = Product("Test", 100)
physical = PhysicalProduct("Test", 100)
digital = DigitalProduct("Test", 100)
assert isinstance(p, PrintableMixin), "Product should inherit from PrintableMixin"
assert isinstance(p, DiscountMixin), "Product should inherit from DiscountMixin"
assert isinstance(physical, Product), "PhysicalProduct should inherit from Product"
assert isinstance(physical, ShippableMixin), "PhysicalProduct should inherit from ShippableMixin"
assert isinstance(physical, PrintableMixin), "PhysicalProduct should inherit from PrintableMixin through Product"
assert isinstance(physical, DiscountMixin), "PhysicalProduct should inherit from DiscountMixin through Product"
assert isinstance(digital, Product), "DigitalProduct should inherit from Product"
assert isinstance(digital, PrintableMixin), "DigitalProduct should inherit from PrintableMixin through Product"
assert isinstance(digital, DiscountMixin), "DigitalProduct should inherit from DiscountMixin through Product"
print("Inheritance test passed!")
def test_method_overriding():
# Uji perilaku penimpaan method
p = Product("Regular Product", 100)
digital = DigitalProduct("Digital Product", 100)
# Harga sama, persentase diskon sama, hasil berbeda
assert p.apply_discount(20) == 80, f"Regular discount calculation failed: {p.apply_discount(20)}"
assert digital.apply_discount(20) == 90, f"Digital fixed discount failed: {digital.apply_discount(20)}"
# Produk digital harus selalu menerapkan diskon 10% terlepas dari parameter
assert digital.apply_discount(0) == 90, "Digital product should apply 10% discount even with 0%"
assert digital.apply_discount(50) == 90, "Digital product should apply 10% discount even with 50%"
print("Method overriding test passed!")
def test_polymorphism():
# Uji perilaku polimorfik dengan list berisi berbagai jenis produk
products = [
Product("Regular", 100),
PhysicalProduct("Physical", 100),
DigitalProduct("Digital", 100)
]
# Atur berat produk fisik
products[1].set_weight(10)
# Hasil yang diharapkan untuk apply_discount(20)
expected_discounts = [80, 80, 90]
for i, product in enumerate(products):
# Semuanya harus memiliki method print_details
assert "Product:" in product.print_details(), f"Polymorphic print_details failed for {type(product)}"
# Semuanya harus memiliki method apply_discount, tetapi dengan implementasi yang berbeda
assert product.apply_discount(20) == expected_discounts[i], f"Polymorphic apply_discount failed for {type(product)}"
print("Polymorphism test passed!")
def test_attribute_access():
# Uji pola akses atribut
p = Product("Test Product", 100)
assert p.name == "Test Product", "Name attribute not properly set in Product"
assert p.price == 100, "Price attribute not properly set in Product"
physical = PhysicalProduct("Physical Product", 200)
assert physical.name == "Physical Product", "Name attribute not properly set in PhysicalProduct"
assert physical.price == 200, "Price attribute not properly set in PhysicalProduct"
# Atribut weight tidak boleh ada sebelum set_weight dipanggil
try:
weight = physical.weight
assert False, "Weight attribute should not exist before set_weight is called"
except AttributeError:
pass
physical.set_weight(15)
assert physical.weight == 15, "Weight attribute not properly set in PhysicalProduct"
digital = DigitalProduct("Digital Product", 300)
assert digital.name == "Digital Product", "Name attribute not properly set in DigitalProduct"
assert digital.price == 300, "Price attribute not properly set in DigitalProduct"
print("Attribute access test passed!")
# Penjalankan utama pengujian
test_case = input()
if test_case == "basic_test":
test_basic_functionality()
elif test_case == "edge_cases":
test_edge_cases()
elif test_case == "large_values":
test_large_values()
elif test_case == "inheritance":
test_inheritance()
elif test_case == "method_overriding":
test_method_overriding()
elif test_case == "polymorphism":
test_polymorphism()
elif test_case == "attribute_access":
test_attribute_access()Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1Dasar-Dasar OOP
File EksternalPengantar OOPclass vs objectParameter selfmethodAtributMethod Konstruktor (__init__)Rangkuman: Kalkulator Sederhana4Pewarisan
Pewarisan DasarFungsi super()Penimpaan MethodPewarisan BergandaUrutan Resolusi MethodRingkasan: Hierarki Karyawan5Polimorfisme
Meninjau Ulang Method OverridingDuck Typingclass dan method AbstrakDesain InterfaceRingkasan: Kalkulator Bentuk8Konsep OOP Lanjutan
Komposisi vs PewarisanMixinsMetode Statis dan Metode ClassDecorator ClassPengelola Konteks11Pola Desain Bagian 2
Pola PerintahPola AdapterPola DecoratorPola Template MethodPola StatePola Composite3Property class
Variabel instance vs variabel classDekorator propertyAtribut privatRangkuman: Pengelola Rekening Bank6Enkapsulasi
Anggota Public, Protected, PrivateModifier AksesPenyembunyian InformasiDecorator Property Tingkat LanjutUlasan: Sistem Catatan Siswa12Proyek: Manajemen Perpustakaan
Ikhtisar Proyekclass Buku dan PenggunaSistem PeminjamanFitur PencarianAntarmuka AdminPengujian dan IntegrasiLatihan sendiri: Compiler Python online