Mixin
Thuộc phần Lập trình hướng đối tượng trong hành trình Python của Coddy. Bài học 38 trên 64.
Mixin là một dạng kế thừa đa lớp đặc biệt, dùng để bổ sung chức năng cho các lớp. Mixin cung cấp những phương thức cụ thể nhưng bản thân chúng không phải là lớp hoàn chỉnh.
Đây là ví dụ về một mixin đơn giản:
class JSONSerializableMixin:
def to_json(self):
import json
return json.dumps(self.__dict__)Hãy cùng phân tích chức năng của mixin này:
self.__dict__- Thuộc tính đặc biệt này chứa một từ điển gồm tất cả thuộc tính của đối tượng và giá trị tương ứngjson.dumps()- Hàm này chuyển đổi từ điển Python thành chuỗi có định dạng JSON- Mixin này đưa chức năng tuần tự hóa JSON vào bất kỳ lớp nào kế thừa từ nó
Bây giờ, hãy xem nó hoạt động:
class User(JSONSerializableMixin):
def __init__(self, name, email):
self.name = name
self.email = emailMixin bổ sung chức năng JSON cho mọi lớp kế thừa từ nó:
user = User("Alice", "alice@example.com")
print(user.to_json())Đầu ra:
{"name": "Alice", "email": "alice@example.com"}Điểm mấu chốt là lớp User hiện có phương thức to_json() mà không cần tự định nghĩa phương thức đó. Mixin đã thêm chức năng này vào lớp!
Tạo nhiều mixin cho các chức năng khác nhau:
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__Mỗi mixin truy cập self.__dict__ để làm việc với các thuộc tính của đối tượng, bất kể lớp nào sử dụng mixin đó. Đây là sức mạnh của mixin: chúng cung cấp chức năng có thể tái sử dụng và hoạt động với thuộc tính của bất kỳ lớp nào.
Kết hợp nhiều mixin trong một lớp:
class Product(JSONSerializableMixin, PrintableMixin, ComparableMixin):
def __init__(self, name, price):
self.name = name
self.price = price
product1 = Product("Laptop", 999)
product2 = Product("Laptop", 999)Sử dụng tất cả chức năng của mixin:
print(product1.to_json()) # Từ JSONSerializableMixin
product1.pretty_print() # Từ PrintableMixin
print(product1 == product2) # Từ ComparableMixinĐầu ra:
{"name": "Laptop", "price": 999}
name: Laptop
price: 999
TrueĐặc điểm chính của mixin:
- Không dùng để tạo đối tượng riêng lẻ
- Cung cấp chức năng cụ thể, có thể tái sử dụng
- Thường không có phương thức
__init__ - Tên thường kết thúc bằng "Mixin" hoặc "able"
- Có thể kết hợp với kế thừa đa lớp
- Dùng
self.__dict__hoặc các tính năng phổ biến khác của đối tượng để linh hoạt hơn
Điểm chính: Mixin giúp chia sẻ chức năng giữa các hệ thống phân cấp lớp khác nhau mà không tạo ra cấu trúc kế thừa phức tạp. Mixin cho phép bạn thêm các khả năng cụ thể như tuần tự hóa, so sánh hoặc in ra vào bất kỳ lớp nào cần chúng. Cách này khuyến khích tái sử dụng code và giúp các lớp tập trung vào trách nhiệm chính.
Thử thách
Trung bìnhTrong thử thách này, bạn sẽ triển khai một hệ thống thương mại điện tử đơn giản bằng cách sử dụng các mixin và tính kế thừa.
Triển khai các lớp cần thiết trong những tệp này (làm theo chú thích TODO trong từng tệp):
printablemixin.py- Tạo PrintableMixin với chức năng định dạng đầu radiscountmixin.py- Triển khai DiscountMixin để tính mức giảm giáshippablemixin.py- Tạo ShippableMixin với các tính năng về trọng lượng và phí vận chuyểnproduct.py- Phát triển lớp Product cơ sở với tính kế thừa mixin phù hợpphysicalproduct.py- Tạo lớp PhysicalProduct kế thừa Productdigitalproduct.py- Triển khai lớp DigitalProduct với cách giảm giá đặc biệt
Tự làm thử
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():
# Kiểm tra chức năng cơ bản của tất cả các lớp
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():
# Kiểm tra các trường hợp đặc biệt như giá trị 0 và giá trị âm
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():
# Kiểm tra với các giá trị rất lớn
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():
# Kiểm tra quan hệ kế thừa
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():
# Kiểm tra cách hoạt động khi ghi đè phương thức
p = Product("Regular Product", 100)
digital = DigitalProduct("Digital Product", 100)
# Cùng giá, cùng tỷ lệ giảm giá nhưng kết quả khác nhau
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)}"
# Sản phẩm kỹ thuật số luôn áp dụng mức giảm giá 10% bất kể tham số
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():
# Kiểm tra tính đa hình bằng danh sách các loại sản phẩm khác nhau
products = [
Product("Regular", 100),
PhysicalProduct("Physical", 100),
DigitalProduct("Digital", 100)
]
# Đặt trọng lượng cho sản phẩm vật lý
products[1].set_weight(10)
# Kết quả mong đợi của apply_discount(20)
expected_discounts = [80, 80, 90]
for i, product in enumerate(products):
# Tất cả đều phải có phương thức print_details
assert "Product:" in product.print_details(), f"Polymorphic print_details failed for {type(product)}"
# Tất cả đều phải có phương thức apply_discount nhưng cách triển khai khác nhau
assert product.apply_discount(20) == expected_discounts[i], f"Polymorphic apply_discount failed for {type(product)}"
print("Polymorphism test passed!")
def test_attribute_access():
# Kiểm tra cách truy cập thuộc tính
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"
# Thuộc tính trọng lượng không nên tồn tại trước khi gọi set_weight
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!")
# Phần chạy test chính
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()Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTham số selfPhương thứcThuộc tínhPhương thức khởi tạo (__init__)Ôn tập: máy tính đơn giản4Kế thừa
Kế thừa cơ bảnHàm super()Ghi đè phương thứcKế thừa đa lớpThứ tự phân giải phương thứcÔn tập: hệ thống phân cấp nhân viên2Decorator
Giới thiệu về decoratorDecorator thuộc tínhDecorator phương thức tĩnhDecorator phương thức lớp5Tính đa hình
Xem lại cách ghi đè phương thứcKiểu gõ vịtLớp và phương thức trừu tượngThiết kế giao diệnÔn tập: máy tính hình học8Các khái niệm OOP nâng cao
Kết hợp và kế thừaMixinPhương thức tĩnh và phương thức lớpDecorator lớpTrình quản lý ngữ cảnhTự luyện tập: Trình biên dịch Python online