Menu
Coddy logo textTech

Mixins

Coddy'nin Python Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 38 / 64.

Mixin'ler, sınıflara ek işlevsellik "katmak" için kullanılan özel bir çoklu kalıtım türüdür. Kendileri tam sınıflar 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ı parçalara ayıralım:

  • self.__dict__ - Bu özel öznitelik, nesnenin tüm özniteliklerini ve bunların değerlerini içeren bir sözlük barındırır
  • json.dumps() - Bu işlev, Python sözlüğünü JSON biçimli bir dizeye dönüştürür
  • Mixin, bu JSON serileştirme işlevselliğini kendisinden kalıtım alan herhangi bir sınıfa "karıştırır"

Şimdi bunu iş başında görelim:

class User(JSONSerializableMixin):
    def __init__(self, name, email):
        self.name = name
        self.email = email

Mixin, ondan 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 içgörü: User sınıfı artık to_json() metoduna, onu doğrudan tanımlamadan sahip. Mixin bu işlevi “karıştırarak ekledi”!

Farklı işlevler 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 mixin, hangi sınıfın mixin'i kullandığından bağımsız olarak nesnenin öznitelikleriyle çalışmak için self.__dict__ erişir. Mixin'lerin gücü budur: herhangi bir sınıfın öznitelikleriyle çalışan yeniden kullanılabilir işlevsellik sağlarlar.

Bir sınıfta birden fazla mixin'i 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
True

Mixin'lerin temel özellikleri:

  • Kendi başlarına örneklenmeleri amaçlanmaz
  • Belirli, yeniden kullanılabilir işlevler sağlarlar
  • Genellikle __init__ metotlarına sahip olmazlar
  • Adları genellikle "Mixin" veya "able" ile biter
  • Çoklu kalıtımla birleştirilebilirler
  • Esnek olmak için self.__dict__ veya diğer yaygın nesne özellikleriyle çalışırlar

Temel Nokta: Mixin'ler, karmaşık kalıtım ağaçları oluşturmadan farklı sınıf hiyerarşileri arasında işlevselliği 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 "karıştırmanıza" olanak tanır. Bu, kodun yeniden kullanımını teşvik eder ve sınıfların birincil sorumluluklarına odaklanmasını sağlar.

challenge icon

Görev

Orta

Bu görevde, mixin'ler ve kalıtım kullanarak basit bir e-ticaret sistemi uygulayacaksın.

Bu dosyalarda gerekli sınıfları uygula (her dosyadaki TODO yorumlarını takip et):

  • printablemixin.py - Biçimlendirilmiş çıktı işlevine sahip PrintableMixin'i oluştur
  • discountmixin.py - Fiyat indirimi hesaplamaları için DiscountMixin'i uygula
  • shippablemixin.py - Ağırlık ve kargo ücreti özellikleri için ShippableMixin'i oluştur
  • product.py - Uygun mixin kalıtımına sahip temel Product sınıfını geliştir
  • physicalproduct.py - Product sınıfını genişleten PhysicalProduct sınıfını oluştur
  • digitalproduct.py - Özel indirim davranışına sahip DigitalProduct sınıfını uygula
GEREKLİ ÇIKTI BİÇİMİ: [Çevrilmiş içeriğiniz burada]

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():
    # Test basic functionality of all classes
    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 kenar durumlarını 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():
    # Test method overriding behavior
    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ı
    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 weight 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):
        # Hepsi print_details metoduna sahip olmalı
        assert "Product:" in product.print_details(), f"Polymorphic print_details failed for {type(product)}"
        
        # Hepsi apply_discount metoduna sahip 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():
    # Test attribute access patterns
    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 var olmamalı
    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()
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler

Kendi başına pratik yap: Online Python derleyicisi