my_car = Car("Red", "Toyota", "Camry") print(my_car.color) # Output: Red my_car.start_engine() # Output: The engine is started.
Metaprogramming allows code to manipulate code. In Python, classes are objects generated by a higher-level construct called a metaclass. By default, Python uses the type metaclass to build classes. Custom Metaclasses
from abc import ABC, abstractmethod class DatabaseConnector(ABC): @abstractmethod def connect(self): """Must be implemented by subclasses.""" pass class PostgreSQLConnector(DatabaseConnector): def connect(self): print("Connected to PostgreSQL.") Use code with caution. Key Takeaways for High-Quality Code
The is the "magic" behind properties, methods, and even super() . A descriptor is an object that defines any of the __get__ , __set__ , or __delete__ methods.
__len__ , __getitem__ , __setitem__ , __iter__ . Callable Objects: __call__ . Summary of Key Takeaways python 3 deep dive part 4 oop high quality
class Point: def __init__(self, x, y, color="red"): self.x = x self.y = y self.color = color
class Descriptor: def __get__(self, instance, owner_class): ... def __set__(self, instance, value): ... def __delete__(self, instance): ...
Metaclasses are the classes of classes. They define how classes are created.
When you create a class, Python calls the metaclass's __new__ and __init__ methods to construct the class object. By creating a custom metaclass (a subclass of type ), you can intercept and customize the creation process. my_car = Car("Red", "Toyota", "Camry") print(my_car
: Use @property for simple, one-off logic. Use custom descriptors to reuse complex validation logic across multiple classes.
@celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("Below absolute zero") self._celsius = value
Faster attribute access and significantly lower memory footprint—essential when instantiating millions of objects. 2. Descriptors: The Secret Behind We all use , but do you know how it actually works? It’s a Descriptor . Understanding the Descriptor Protocol ( __delete__
: Those who have completed Part 1 (Functional Python) or have a strong grasp of Python's basic syntax and functional programming concepts. By default, Python uses the type metaclass to build classes
p = Point(3, 4) p.z = 5 # AttributeError: 'Point' has no attribute 'z'
class StrictDocstringMeta(type): def __new__(cls, name, bases, dct): # Intercept class creation if not dct.get('__doc__'): raise TypeError(f"Class 'name' must have a docstring documentation.") return super().__new__(cls, name, bases, dct) # This works perfectly: class ValidClass(metaclass=StrictDocstringMeta): """This class is correctly documented.""" pass # This throws a TypeError immediately during module import: # class InvalidClass(metaclass=StrictDocstringMeta): # pass Use code with caution. 5. Pythonic Design Patterns
num = 42 print(num.__class__) # Output: print(type(num)) # Output: Use code with caution.
Duck typing is a cornerstone of Python: If it looks like a duck and quacks like a duck, it must be a duck . This means the type or class of an object is less important than the methods it defines, focusing on what an object can do rather than what it is. While this offers immense flexibility, it also has drawbacks like a lack of clarity and errors that are only caught at runtime.
Python resolves method lookups in multiple inheritance hierarchies using the C3 Linearization algorithm.
class A: pass class B(A): pass class C(A): pass class D(B, C): pass