OOP Advanced
Mixins
Composable Behavior
You have 10 classes that need JSON serialization. Instead of copying code to
each, create a JsonMixin with to_json() method. Any class can mix it in
to gain that capability - composition over inheritance.
Basic mixin
A small class that adds one capability.
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
# Mixin Basics
def main():
print("=== Mixin Basics ===\n")
# What is a mixin?
print("--- What is a Mixin? ---")
print("A small class that provides specific functionality")
print("to be 'mixed into' other classes.\n")
# Simple mixin example
print("--- Simple Mixin Example ---")
# Product with RepresentationMixin
product =
print(f"Product info: {product.get_info()}")
print(f"Repr: {product!r}")
# Order with RepresentationMixin
order = Order("ORD-001", 5)
print(f"Order info: {order.get_info()}")
print(f"Repr: {order!r}")
# Mixin adds functionality to different classes
print("\n--- Same Mixin, Different Classes ---")
# Cat with VoiceMixin
cat =
print(f"{cat.name}: ", end="")
cat.speak()
cat.whisper()
# Dog with VoiceMixin
dog = Dog("Buddy")
print(f"{dog.name}: ", end="")
dog.speak()
dog.whisper()
# Mixin naming convention
print("\n--- Naming Convention ---")
print("Mixins typically end with 'Mixin':")
print(" • LoggingMixin")
print(" • SerializableMixin")
print(" • ComparableMixin")
print(" • RepresentationMixin")
print("\n=== Key Points ===")
print("""
1. Mixins add specific functionality
2. Usually don't have __init__ (or minimal)
3. Designed to be combined with other classes
4. Name ends with 'Mixin' by convention
5. Focus on ONE responsibility per mixin
""")
# RepresentationMixin - adds string representations
class RepresentationMixin:
"""Mixin that provides string representation methods."""
def get_info(self):
"""Return formatted info string."""
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
return f"{self.__class__.__name__}({attrs})"
def __repr__(self):
"""Return debug representation."""
return self.get_info()
# VoiceMixin - adds voice capabilities
class VoiceMixin:
"""Mixin that provides voice-related methods."""
sound = "..."
def speak(self):
"""Make the sound loudly."""
print(f"{self.sound}!")
def whisper(self):
"""Make the sound quietly."""
print(f"({self.sound.lower()})")
# Classes using RepresentationMixin
class Product(RepresentationMixin):
"""Product class with representation mixin."""
def __init__(self, name: str, price: float):
self.name = name
self.price = price
class Order(RepresentationMixin):
"""Order class with representation mixin."""
def __init__(self, order_id: str, quantity: int):
self.order_id = order_id
self.quantity = quantity
# Classes using VoiceMixin
class Cat(VoiceMixin):
"""Cat class with voice mixin."""
sound = "Meow"
def __init__(self, name: str):
self.name = name
class Dog(VoiceMixin):
"""Dog class with voice mixin."""
sound = "Woof"
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
Mixins add single behavior. Usually no __init__, just methods.
Composing mixins
Combine multiple mixins for multiple capabilities.
# Composing Multiple Mixins
from datetime import datetime
def main():
print("=== Composing Multiple Mixins ===\n")
# Multiple mixins in one class
print("--- Multiple Mixins Combined ---")
# DataRecord with multiple mixins
record = DataRecord(1, "Sample data", "active")
print("DataRecord capabilities:")
print(f" As dict: {record.to_dict()}")
print(f" Timestamp: {record.created_at}")
record.log("Record created")
# Compare with DataRecord without mixins
print("\n--- Without Mixins vs With Mixins ---")
simple = SimpleRecord(1, "Simple")
print(f"SimpleRecord: {simple}")
enhanced = EnhancedRecord(1, "Enhanced")
print(f"EnhancedRecord: {enhanced}")
print(f" as_json: {enhanced.as_json()}")
print(f" created_at: {enhanced.created_at}")
# Mixin order matters
print("\n--- Mixin Order (MRO) ---")
print("class DataRecord(DictMixin, TimestampMixin, LoggingMixin):")
print(f"MRO: {[c.__name__ for c in DataRecord.__mro__]}")
# User with all mixins
print("\n--- User with Multiple Mixins ---")
user = User(42, "alice", "alice@example.com")
print(f"User dict: {user.to_dict()}")
print(f"Has timestamp: {hasattr(user, 'created_at')}")
user.log("User loaded from database")
# Selective mixin usage
print("\n--- Selective Mixin Usage ---")
# LogOnly class only has logging
logger = LogOnly("Logger instance")
logger.log("Testing selective mixin")
print(f"Has to_dict? {hasattr(logger, 'to_dict')}")
print(f"Has created_at? {hasattr(logger, 'created_at')}")
print("\n=== Key Points ===")
print("""
1. Combine multiple mixins for rich functionality
2. Each mixin adds specific capability
3. Order in inheritance affects MRO
4. Pick only the mixins you need
5. Mixins should be independent (no mixin depends on another)
""")
# Define individual mixins
class DictMixin:
"""Mixin for dictionary conversion."""
def to_dict(self) -> dict:
"""Convert object to dictionary."""
return {k: v for k, v in self.__dict__.items()
if not k.startswith('_')}
def as_json(self) -> str:
"""Return JSON-like string representation."""
import json
return json.dumps(self.to_dict(), default=str)
class TimestampMixin:
"""Mixin for automatic timestamps."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.created_at = datetime.now()
class LoggingMixin:
"""Mixin for logging capability."""
def log(self, message: str):
"""Log a message with class name prefix."""
print(f"[{self.__class__.__name__}] {message}")
# Class combining multiple mixins
class DataRecord(DictMixin, TimestampMixin, LoggingMixin):
"""Data record with dict, timestamp, and logging capabilities."""
def __init__(self, record_id: int, data: str, status: str):
super().__init__()
self.record_id = record_id
self.data = data
self.status = status
# Comparison: without vs with mixins
class SimpleRecord:
"""Record without any mixins."""
def __init__(self, record_id: int, data: str):
self.record_id = record_id
self.data = data
def __str__(self):
return f"SimpleRecord({self.record_id}, {self.data})"
class EnhancedRecord(DictMixin, TimestampMixin):
"""Record with dict and timestamp mixins."""
def __init__(self, record_id: int, data: str):
super().__init__()
self.record_id = record_id
self.data = data
def __str__(self):
return f"EnhancedRecord({self.record_id}, {self.data})"
# User class with all mixins
class User(DictMixin, TimestampMixin, LoggingMixin):
"""User with full mixin support."""
def __init__(self, user_id: int, username: str, email: str):
super().__init__()
self.user_id = user_id
self.username = username
self.email = email
# Selective mixin usage
class LogOnly(LoggingMixin):
"""Class with only logging mixin."""
def __init__(self, name: str):
self.name = name
if __name__ == "__main__":
main()
class MyClass(MixinA, MixinB, Base): - gets all mixin methods.
Mixin with super()
Cooperative mixins that chain properly.
# Using super() in Mixins
def main():
print("=== Using super() in Mixins ===\n")
# Why super() in mixins?
print("--- Why super() in Mixins? ---")
print("When mixins need to participate in __init__ chain,")
print("super() ensures all __init__ methods are called.\n")
# Without super() - broken chain
print("--- Without super() (Broken) ---")
try:
broken = BrokenExample("test")
print(f"value: {broken.value}")
print(f"initialized: {getattr(broken, 'initialized', 'MISSING')}")
print(f"timestamped: {getattr(broken, 'timestamped', 'MISSING')}")
except AttributeError as e:
print(f"Error: {e}")
# With super() - proper chain
print("\n--- With super() (Correct) ---")
correct = CorrectExample("test")
print(f"value: {correct.value}")
print(f"initialized: {correct.initialized}")
print(f"timestamped: {correct.timestamped}")
# The **kwargs pattern
print("\n--- The **kwargs Pattern ---")
print("Pass extra kwargs through super() to next class:")
print("""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.my_attr = ...
""")
# Full cooperative example
print("--- Full Cooperative Example ---")
entity = TrackedEntity(name="Widget", category="product")
print(f"Entity: name={entity.name}, category={entity.category}")
print(f" has_id: {entity.entity_id[:8]}...")
print(f" created_at: {entity.created_at}")
print(f" initialized: {entity.initialized}")
# MRO walkthrough
print("\n--- MRO Walkthrough ---")
print(f"TrackedEntity MRO: {[c.__name__ for c in TrackedEntity.__mro__]}")
print("""
Call order:
1. TrackedEntity.__init__(name="Widget", category="product")
2. → IdMixin.__init__(category="product") [name consumed]
3. → TimestampMixin.__init__() [category consumed]
4. → InitMixin.__init__()
5. → object.__init__()
""")
print("\n=== Key Points ===")
print("""
1. Always use super().__init__(**kwargs) in mixins
2. Consume your parameters, pass the rest via **kwargs
3. All __init__ methods get called in MRO order
4. Last mixin in chain should call super() too
5. This pattern enables true cooperative inheritance
""")
# Broken: Mixins without proper super()
class BrokenMixinA:
"""Mixin that DOESN'T use super()."""
def __init__(self):
self.initialized = True
# No super() call! Chain breaks here.
class BrokenMixinB:
"""Another mixin that DOESN'T use super()."""
def __init__(self):
self.timestamped = True
# No super() call!
class BrokenExample(BrokenMixinA, BrokenMixinB):
"""BrokenMixinB.__init__ never called!"""
def __init__(self, value):
super().__init__()
self.value = value
# Correct: Mixins with proper super()
class CorrectMixinA:
"""Mixin that properly uses super()."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.initialized = True
class CorrectMixinB:
"""Another mixin that properly uses super()."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.timestamped = True
class CorrectExample(CorrectMixinA, CorrectMixinB):
"""Both mixin __init__ methods get called!"""
def __init__(self, value, **kwargs):
super().__init__(**kwargs)
self.value = value
# Full cooperative inheritance example
import uuid
from datetime import datetime
class InitMixin:
"""Base mixin - ends the super() chain."""
def __init__(self, **kwargs):
super().__init__()
self.initialized = True
class TimestampMixin:
"""Adds creation timestamp."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.created_at = datetime.now().isoformat()
class IdMixin:
"""Adds unique ID."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.entity_id = str(uuid.uuid4())
class TrackedEntity(IdMixin, TimestampMixin, InitMixin):
"""Entity with ID, timestamp, and init tracking."""
def __init__(self, name: str, category: str, **kwargs):
super().__init__(**kwargs)
self.name = name
self.category = category
if __name__ == "__main__":
main()
super() ensures all mixins in chain get called.
Common mixin patterns
Serialization, comparison, logging mixins.
# Common Mixin Patterns
import json
from datetime import datetime
def main():
print("=== Common Mixin Patterns ===\n")
# 1. Serialization Mixin
print("--- 1. Serialization Mixin ---")
user = User(1, "alice", "alice@example.com")
print(f"Original: {user}")
print(f"to_dict: {user.to_dict()}")
print(f"to_json: {user.to_json()}")
# Reconstruct from dict
data = user.to_dict()
user2 = User.from_dict(data)
print(f"Reconstructed: {user2}")
# 2. Comparison Mixin
print("\n--- 2. Comparison Mixin ---")
v1 = Version(1, 0, 0)
v2 = Version(1, 2, 0)
v3 = Version(1, 0, 0)
print(f"v1 = {v1}")
print(f"v2 = {v2}")
print(f"v3 = {v3}")
print(f"v1 == v3: {v1 == v3}")
print(f"v1 < v2: {v1 < v2}")
print(f"v2 > v1: {v2 > v1}")
print(f"v1 <= v3: {v1 <= v3}")
# Sorting works too
versions = [v2, v1, v3]
print(f"Sorted: {sorted(versions)}")
# 3. Validation Mixin
print("\n--- 3. Validation Mixin ---")
product = Product("Laptop", 999.99, 10)
print(f"Valid product: {product}")
try:
invalid = Product("", -100, -5)
except ValueError as e:
print(f"Validation failed: {e}")
# 4. Repr Mixin
print("\n--- 4. Repr Mixin ---")
item = Item("Widget", 42)
print(f"repr: {item!r}")
print(f"str: {item}")
print("\n=== Common Mixin Use Cases ===")
print("""
• SerializableMixin: Convert to/from dict, JSON, XML
• ComparableMixin: Enable <, >, ==, <=, >= operators
• ValidatableMixin: Add validation methods
• ReprMixin: Auto-generate __repr__ and __str__
• HashableMixin: Make objects hashable (for sets/dicts)
• CopyableMixin: Deep/shallow copy support
""")
# 1. Serialization Mixin
class SerializableMixin:
"""Mixin for JSON serialization/deserialization."""
def to_dict(self) -> dict:
"""Convert to dictionary."""
return {k: v for k, v in self.__dict__.items()
if not k.startswith('_')}
def to_json(self) -> str:
"""Convert to JSON string."""
return json.dumps(self.to_dict(), default=str)
@classmethod
def from_dict(cls, data: dict):
"""Create instance from dictionary."""
return cls(**data)
# 2. Comparison Mixin
class ComparableMixin:
"""Mixin that enables comparison operators.
Requires: _compare_key() method in the class.
"""
def _compare_key(self):
"""Override this to define comparison key."""
raise NotImplementedError("Subclass must implement _compare_key()")
def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return self._compare_key() == other._compare_key()
def __lt__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return self._compare_key() < other._compare_key()
def __le__(self, other):
return self == other or self < other
def __gt__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return self._compare_key() > other._compare_key()
def __ge__(self, other):
return self == other or self > other
# 3. Validation Mixin
class ValidatableMixin:
"""Mixin that adds validation support."""
def validate(self):
"""Validate the object. Override _get_validations()."""
errors = []
for field, value, check, message in self._get_validations():
if not check(value):
errors.append(f"{field}: {message}")
if errors:
raise ValueError("; ".join(errors))
def _get_validations(self):
"""Return list of (field, value, check_func, error_msg)."""
return []
# 4. Repr Mixin
class ReprMixin:
"""Mixin that auto-generates __repr__ and __str__."""
def __repr__(self):
attrs = ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items()
if not k.startswith('_'))
return f"{self.__class__.__name__}({attrs})"
def __str__(self):
return self.__repr__()
# Classes using the mixins
class User(SerializableMixin):
"""User with serialization support."""
def __init__(self, user_id: int, username: str, email: str):
self.user_id = user_id
self.username = username
self.email = email
def __str__(self):
return f"User({self.user_id}, {self.username})"
class Version(ComparableMixin):
"""Version with comparison support."""
def __init__(self, major: int, minor: int, patch: int):
self.major = major
self.minor = minor
self.patch = patch
def _compare_key(self):
"""Comparison key: tuple of (major, minor, patch)."""
return (self.major, self.minor, self.patch)
def __repr__(self):
return f"v{self.major}.{self.minor}.{self.patch}"
class Product(ValidatableMixin):
"""Product with validation support."""
def __init__(self, name: str, price: float, quantity: int):
self.name = name
self.price = price
self.quantity = quantity
self.validate()
def _get_validations(self):
"""Define product validations."""
return [
("name", self.name, lambda x: x and len(x) > 0, "Name is required"),
("price", self.price, lambda x: x >= 0, "Price must be non-negative"),
("quantity", self.quantity, lambda x: x >= 0, "Quantity must be non-negative"),
]
def __str__(self):
return f"Product({self.name}, ${self.price}, qty={self.quantity})"
class Item(ReprMixin):
"""Item with auto-generated repr."""
def __init__(self, name: str, value: int):
self.name = name
self.value = value
if __name__ == "__main__":
main()
Popular patterns: JsonMixin, ComparableMixin, LoggingMixin.
Mixin vs inheritance
When to use each approach.
# Mixin vs Regular Inheritance
def main():
print("=== Mixin vs Regular Inheritance ===\n")
# When to use regular inheritance
print("--- When to Use Regular Inheritance ---")
print("Use when: IS-A relationship exists")
print("Example: Dog IS-A Animal\n")
dog = Dog("Buddy")
print(f"{dog.name} says: ", end="")
dog.speak()
dog.move()
# When to use mixins
print("\n--- When to Use Mixins ---")
print("Use when: Adding CAPABILITY (HAS-A behavior)")
print("Example: Dog HAS logging capability\n")
logged_dog = LoggedDog("Max")
logged_dog.log("Dog created")
logged_dog.speak()
# Comparison: Same behavior, different approaches
print("\n--- Comparison: Two Approaches ---")
print("Goal: Add serialization to multiple unrelated classes\n")
# Approach 1: Base class (problematic)
print("Approach 1: Shared Base Class")
print(" class User(SerializableBase): ...")
print(" class Product(SerializableBase): ...")
print(" Problem: What if User already has a base class?")
print(" Problem: SerializableBase isn't really a 'parent'\n")
# Approach 2: Mixin (better)
print("Approach 2: Mixin")
print(" class User(UserBase, SerializableMixin): ...")
print(" class Product(ProductBase, SerializableMixin): ...")
print(" Benefit: Can add to any class hierarchy")
print(" Benefit: Clear that it's adding capability, not identity\n")
# Demonstrate with real classes
print("--- Real Example ---")
user = User("alice", "alice@example.com")
product = Product("Laptop", 999.99)
print(f"User: {user.get_name()}")
print(f"User serialized: {user.to_dict()}")
print(f"Product: {product.get_name()}")
print(f"Product serialized: {product.to_dict()}")
# Decision guide
print("\n=== Decision Guide ===")
print("""
Use REGULAR INHERITANCE when:
┌─────────────────────────────────────────────────┐
│ • Clear IS-A relationship (Dog IS-A Animal) │
│ • Shared state and behavior │
│ • Objects are naturally in same hierarchy │
│ • Want to override parent methods │
└─────────────────────────────────────────────────┘
Use MIXIN when:
┌─────────────────────────────────────────────────┐
│ • Adding capability (HAS-A behavior) │
│ • Unrelated classes need same feature │
│ • Small, focused functionality │
│ • No shared identity/state │
│ • Want to compose behaviors flexibly │
└─────────────────────────────────────────────────┘
""")
# Anti-patterns
print("=== Anti-Patterns to Avoid ===")
print("""
✗ Mixin with complex state (use regular class)
✗ Mixin that depends on other mixins (order issues)
✗ Too many mixins (hard to understand)
✗ Using inheritance just for code reuse
✗ Deep inheritance hierarchies
""")
# Regular Inheritance Example
class Animal:
"""Base class - defines what an animal IS."""
def __init__(self, name: str):
self.name = name
def speak(self):
"""Override in subclass."""
print("Some sound")
def move(self):
"""Common to all animals."""
print(f"{self.name} is moving")
class Dog(Animal):
"""Dog IS-A Animal - clear inheritance."""
def speak(self):
print("Woof!")
# Mixin Example
class LoggingMixin:
"""Adds logging CAPABILITY."""
def log(self, message: str):
print(f"[{self.__class__.__name__}] {message}")
class LoggedDog(Animal, LoggingMixin):
"""Dog with logging capability added."""
def speak(self):
self.log("About to bark")
print("Woof!")
# Comparison: Adding serialization
class SerializableMixin:
"""Mixin - adds serialization CAPABILITY."""
def to_dict(self) -> dict:
return {k: v for k, v in self.__dict__.items()
if not k.startswith('_')}
# Base classes representing different domains
class Entity:
"""Base for database entities."""
def __init__(self):
self._id = None
def get_name(self) -> str:
raise NotImplementedError
class Item:
"""Base for store items."""
def __init__(self):
self._sku = None
def get_name(self) -> str:
raise NotImplementedError
# Classes using mixin with their own base classes
class User(Entity, SerializableMixin):
"""User IS-A Entity, HAS serialization."""
def __init__(self, username: str, email: str):
super().__init__()
self.username = username
self.email = email
def get_name(self) -> str:
return self.username
class Product(Item, SerializableMixin):
"""Product IS-A Item, HAS serialization."""
def __init__(self, name: str, price: float):
super().__init__()
self.name = name
self.price = price
def get_name(self) -> str:
return self.name
if __name__ == "__main__":
main()
Inheritance: "is-a" relationship. Mixin: "has capability". Choose wisely.
Exercise: practical.py
Build a web framework-style mixin system