Mixins
Coddy'nin Python Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 38 / 64.
Mixin'ler, sınıflara ek işlevsellik "karıştırmak" için kullanılan özel bir çoklu kalıtım türüdür. Kendileri tam bir sınıf olmadan belirli metotlar sağlarlar.
İşte basit bir mixin örneği:
class JSONSerializableMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)Bu mixin'in ne yaptığını inceleyelim:
self.__dict__- Bu özel öznitelik, nesnenin tüm özniteliklerinin ve değerlerinin bir sözlüğünü içerirjson.dumps()- Bu fonksiyon, Python sözlüğünü JSON formatında bir dizeye dönüştürür- Mixin, bu JSON serileştirme işlevselliğini kendisinden miras alan herhangi bir sınıfa "dahil eder" (mixes in)
Şimdi bunu iş başında görelim:
class User(JSONSerializableMixin):
def __init__(self, name, email):
self.name = name
self.email = emailMixin, kendisinden miras alan herhangi bir sınıfa JSON işlevselliği ekler:
user = User("Alice", "alice@example.com")
print(user.to_json())Çıktı:
{"name": "Alice", "email": "alice@example.com"}Temel çıkarım: User sınıfı artık to_json() metoduna, onu doğrudan tanımlamadan sahip. Mixin bu işlevselliği "dahil etti"!
Farklı işlevsellikler için birden fazla mixin oluşturun:
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__Her bir mixin, mixin'i hangi sınıfın kullandığına bakılmaksızın nesnenin öznitelikleriyle çalışmak için self.__dict__ yapısına erişir. Bu, mixin'lerin gücüdür - herhangi bir sınıfın öznitelikleriyle çalışan yeniden kullanılabilir işlevsellik sağlarlar.
Birden fazla mixin'i tek bir sınıfta birleştirin:
class Product(JSONSerializableMixin, PrintableMixin, ComparableMixin):
def __init__(self, name, price):
self.name = name
self.price = price
product1 = Product("Laptop", 999)
product2 = Product("Laptop", 999)Tüm mixin işlevlerini kullanın:
print(product1.to_json()) # JSONSerializableMixin'den
product1.pretty_print() # PrintableMixin'den
print(product1 == product2) # ComparableMixin'denÇıktı:
{"name": "Laptop", "price": 999}
name: Laptop
price: 999
TrueMixin'lerin temel özellikleri:
- Kendi başlarına örneklendirilmek üzere tasarlanmamışlardır
- Belirli, yeniden kullanılabilir işlevsellik sağlarlar
- Genellikle
__init__metotlarına sahip olmazlar - İsimleri genellikle "Mixin" veya "able" ile biter
- Çoklu kalıtım ile birleştirilebilirler
- Esnek olmak için
self.__dict__veya diğer yaygın nesne özellikleriyle çalışırlar
Anahtar Nokta: Mixin'ler, karmaşık kalıtım ağaçları oluşturmadan farklı sınıf hiyerarşileri arasında işlevsellik paylaşmanın bir yolunu sunar. Serileştirme, karşılaştırma veya yazdırma gibi belirli yetenekleri, bunlara ihtiyaç duyan herhangi bir sınıfa "dahil etmenize" (mix in) olanak tanırlar. Bu, kodun yeniden kullanılabilirliğini teşvik eder ve sınıfların birincil sorumluluklarına odaklanmasını sağlar.
Görev
OrtaBu meydan okumada, mixin'leri ve kalıtımı kullanarak basit bir e-ticaret sistemi uygulayacaksınız.
Gerekli sınıfları şu dosyalarda uygulayın (her dosyadaki TODO yorumlarını takip edin):
printablemixin.py- Biçimlendirilmiş çıktı işlevselliğine sahip PrintableMixin'i oluşturundiscountmixin.py- Fiyat indirimi hesaplamaları için DiscountMixin'i uygulayınshippablemixin.py- Ağırlık ve nakliye maliyeti özellikleri için ShippableMixin'i oluşturunproduct.py- Uygun mixin kalıtımı ile temel Product sınıfını geliştirinphysicalproduct.py- Product sınıfını genişleten PhysicalProduct sınıfını oluşturundigitalproduct.py- Özel indirim davranışına sahip DigitalProduct sınıfını uygulayın
Kopya kağıdı
Mixin'ler, sınıfların kendileri tam bir sınıf olmadan sınıflara ek işlevsellik "karıştırmak" (mix in) için kullanılan özel bir çoklu kalıtım türüdür.
Temel mixin örneği:
class JSONSerializableMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)
class User(JSONSerializableMixin):
def __init__(self, name, email):
self.name = name
self.email = emailBirden fazla mixin birleştirilebilir:
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__
class Product(JSONSerializableMixin, PrintableMixin, ComparableMixin):
def __init__(self, name, price):
self.name = name
self.price = priceMixin'lerin temel özellikleri:
- Kendi başlarına örneklenmek (instantiate) için tasarlanmamışlardır
- Belirli, yeniden kullanılabilir işlevsellik sağlarlar
- Genellikle
__init__metotları yoktur - İsimleri genellikle "Mixin" veya "able" ile biter
- Çoklu kalıtım ile birleştirilebilirler
Kendin dene
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():
# Tüm sınıfların temel işlevselliğini test et
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():
# Sıfır ve negatif değerler gibi uç durumları test et
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():
# Çok büyük değerlerle test et
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():
# Kalıtım ilişkilerini test et
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():
# Metot geçersiz kılma (overriding) davranışını test et
p = Product("Regular Product", 100)
digital = DigitalProduct("Digital Product", 100)
# Aynı fiyat, aynı indirim yüzdesi, farklı sonuçlar
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)}"
# Dijital ürün, parametreden bağımsız olarak her zaman %10 indirim uygulamalıdır
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():
# Farklı ürün türlerinden oluşan bir liste ile polimorfik davranışı test et
products = [
Product("Regular", 100),
PhysicalProduct("Physical", 100),
DigitalProduct("Digital", 100)
]
# Fiziksel ürün için ağırlık ayarla
products[1].set_weight(10)
# apply_discount(20) için beklenen sonuçlar
expected_discounts = [80, 80, 90]
for i, product in enumerate(products):
# Hepsinin print_details metodu olmalı
assert "Product:" in product.print_details(), f"Polymorphic print_details failed for {type(product)}"
# Hepsinin apply_discount metodu olmalı ancak farklı uygulamalarla
assert product.apply_discount(20) == expected_discounts[i], f"Polymorphic apply_discount failed for {type(product)}"
print("Polymorphism test passed!")
def test_attribute_access():
# Öznitelik erişim modellerini test et
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"
# set_weight çağrılmadan önce weight özniteliği mevcut olmamalıdır
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!")
# Ana test çalıştırıcı
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()Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Object Oriented Programming bölümündeki tüm dersler
1OOP Temelleri
Harici DosyalarOOP'ye GirişSınıflar ve Nesnelerself ParametresiMetotlarÖzniteliklerYapıcı Metot (__init__)Özet - Basit Hesap Makinesi4Kalıtım
Temel Kalıtımsuper() FonksiyonuMetot Geçersiz KılmaÇoklu KalıtımMetot Çözümleme SırasıÖzet - Çalışan Hiyerarşisi7Özel Metotlar
Sihirli Metotlara GirişOperatör Aşırı YüklemeKapsayıcı Sihirli MetotlarıÖzet - Özel Liste10Tasarım Kalıpları Bölüm 1
Tasarım kalıplarına girişSingleton KalıbıFactory KalıbıObserver KalıbıStrategy Kalıbı13Final Meydan Okumaları
E-öğrenme PlatformuBankacılık SistemiOyun Karakteri GeliştirmeAraç Kiralama Servisi5Çok Biçimlilik (Polymorphism)
Metot Geçersiz Kılmaya Yeniden BakışDuck TypingSoyut Sınıflar ve MetotlarArayüz TasarımıÖzet - Şekil Hesaplayıcı8İleri Düzey OOP Kavramları
Kompozisyon ve KalıtımMixinsStatik ve Sınıf MetotlarıSınıf DekoratörleriBağlam Yöneticileri11Tasarım Kalıpları Bölüm 2
Komut KalıbıAdaptör KalıbıDekoratör KalıbıŞablon Metot KalıbıDurum KalıbıKompozit Kalıbı