Chapter Nine Python Program/Notes =================================== Chapter 9 - Meeting Meta Classes ---------------------------------- - The first line of the chapter is "you don't need to know this". Anyways here's what I know. - A class in an object that creates instances. - A metaclass is an object that creates classes. - The base metaclass is a type. - When a class is made the compiler thing checks thr body to see what class attributes are made. This stuff gets put in a dictionary. - __prepare__ can create the namespacve data structure ahead of time I think?? - There is no real "final class" in Python, but you can use metaclasses for that. - Metaclasses can obviously have attributes and stuff. - I don't get ABC's, so here is what Gemini had to say. An ABC defines what a set of subclasses should do, but it doesn't necessarily tell them how to do it. .. code-block:: python from abc import ABC, abstractmethod # The Blueprint class Animal(ABC): @abstractmethod def make_sound(self): pass def sleep(self): # ABCs can also have normal methods with logic print("Zzz...") # The Implementation class Dog(Animal): def make_sound(self): print("Bark!") # This would cause an error! # my_animal = Animal() # This works perfectly my_dog = Dog() my_dog.make_sound() # Output: Bark! my_dog.sleep() # Output: Zzz... Why Use an ABC? - Enforced Implementation: It forces any child class to implement specific methods. If the child class forgets one, Python will raise an error the moment you try to use it. - Prevents Instantiation: You cannot create an object directly from an ABC. You can't have a generic "Shape" object; you must have a "Circle" or a "Square." - Interface Consistency: It allows you to treat different objects as the same type (Polymorphism) because you are guaranteed they share the same method names. - When you add a __call__ to a class that makes it a function ig. Cuz it functions. - When a class is callable it can be decorated. - You can use a class decorator with parameters. - Singleton Da Program Example ---------------------- .. code-block:: python """ Chapter 9 - Meeting Metaclasses: A Runnable Example Covers: metaclasses, __prepare__, final classes, ABCs, __call__, class decorators, and singletons. """ from abc import ABC, abstractmethod from collections import OrderedDict # ───────────────────────────────────────────── # 1. THE BASE METACLASS IS `type` # Every class is secretly an instance of `type`. # ───────────────────────────────────────────── print("=== 1. type is the base metaclass ===") print(type(int)) # print(type(str)) # class Dog: pass print(type(Dog)) # -- Dog was created by `type` print() # ───────────────────────────────────────────── # 2. CUSTOM METACLASS WITH __prepare__ # __prepare__ lets us supply a custom namespace # (dict) before the class body is executed. # Here we use OrderedDict so attribute insertion # order is guaranteed and visible. # ───────────────────────────────────────────── print("=== 2. Custom metaclass with __prepare__ ===") class OrderedMeta(type): @classmethod def __prepare__(mcs, name, bases): print(f" __prepare__ called for '{name}' — returning OrderedDict") return OrderedDict() # body is written into this dict def __new__(mcs, name, bases, namespace): print(f" Attributes collected in order: {list(namespace.keys())}") return super().__new__(mcs, name, bases, namespace) class Spaceship(metaclass=OrderedMeta): engines = 4 crew = 7 speed = "warp 9" print() # ───────────────────────────────────────────── # 3. FINAL CLASS via metaclass # Python has no built-in `final` keyword, # but a metaclass can raise an error when # someone tries to subclass a marked class. # ───────────────────────────────────────────── print("=== 3. Final class via metaclass ===") class FinalMeta(type): _finals = set() def __new__(mcs, name, bases, namespace): for base in bases: if base in FinalMeta._finals: raise TypeError(f"Cannot subclass '{base.__name__}' — it is final.") cls = super().__new__(mcs, name, bases, namespace) if namespace.get("__final__"): FinalMeta._finals.add(cls) return cls class Vault(metaclass=FinalMeta): __final__ = True secret = "treasure" try: class CrackedVault(Vault): # should explode pass except TypeError as e: print(f" Caught expected error: {e}") print() # ───────────────────────────────────────────── # 4. ABC — Abstract Base Class # Defines what subclasses *must* implement. # Cannot be instantiated directly. # ───────────────────────────────────────────── print("=== 4. ABC ===") class Animal(ABC): @abstractmethod def make_sound(self): """Every animal must implement this.""" ... def sleep(self): # concrete method — shared by all print(" Zzz...") class Cat(Animal): def make_sound(self): print(" Meow!") class Dog(Animal): def make_sound(self): print(" Woof!") # Animal() ← would raise TypeError for animal in [Cat(), Dog()]: animal.make_sound() animal.sleep() print() # ───────────────────────────────────────────── # 5. __call__ — making a class instance callable # Adding __call__ turns an *instance* into # something you can call like a function. # ───────────────────────────────────────────── print("=== 5. __call__ — callable instances ===") class Multiplier: def __init__(self, factor): self.factor = factor def __call__(self, value): # now the instance IS a function return value * self.factor double = Multiplier(2) triple = Multiplier(3) print(f" double(5) = {double(5)}") # 10 print(f" triple(5) = {triple(5)}") # 15 print() # ───────────────────────────────────────────── # 6. CLASS DECORATOR WITH PARAMETERS # A decorator factory: a function that returns # a decorator that wraps the class. # ───────────────────────────────────────────── print("=== 6. Class decorator with parameters ===") def tag(label): """Attach a label to a class and log instantiation.""" def decorator(cls): original_init = cls.__init__ def new_init(self, *args, **kwargs): print(f" [{label}] Creating a {cls.__name__}") original_init(self, *args, **kwargs) cls.__init__ = new_init cls.__tag__ = label return cls return decorator @tag("EXPERIMENTAL") class Rocket: def __init__(self, name): self.name = name r = Rocket("Falcon 9") print(f" Rocket tag: {r.__tag__}") print() # ───────────────────────────────────────────── # 7. SINGLETON via metaclass # Only one instance of the class can ever exist. # The metaclass intercepts __call__ to enforce it. # ───────────────────────────────────────────── print("=== 7. Singleton via metaclass ===") class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in SingletonMeta._instances: print(f" Creating the one-and-only {cls.__name__} instance") SingletonMeta._instances[cls] = super().__call__(*args, **kwargs) else: print(f" Returning existing {cls.__name__} instance") return SingletonMeta._instances[cls] class DatabaseConnection(metaclass=SingletonMeta): def __init__(self, url): self.url = url db1 = DatabaseConnection("postgres://localhost/mydb") db2 = DatabaseConnection("postgres://localhost/mydb") print(f" db1 is db2: {db1 is db2}") # True — same object print(f" URL: {db1.url}") print() # ───────────────────────────────────────────── # SUMMARY # ───────────────────────────────────────────── print("=== Summary ===") print(" type — the built-in metaclass that creates all classes") print(" __prepare__ — customize the namespace dict before class body runs") print(" FinalMeta — enforce 'no subclassing' at the metaclass level") print(" ABC — declare required methods; prevent direct instantiation") print(" __call__ — make instances callable like functions") print(" @tag(label) — parameterized class decorator") print(" SingletonMeta — one instance only, enforced via metaclass __call__")