1"""
2Python Metaclasses
3
4Demonstrates:
5- type() - the metaclass of all classes
6- Custom metaclasses
7- __init_subclass__ hook
8- Class creation process
9- Practical examples (registry, singleton, validation)
10- ABCMeta
11"""
12
13from typing import Any, Dict
14from abc import ABCMeta, abstractmethod
15
16
17def section(title: str) -> None:
18 """Print a section header."""
19 print("\n" + "=" * 60)
20 print(f" {title}")
21 print("=" * 60)
22
23
24# =============================================================================
25# Understanding type()
26# =============================================================================
27
28section("Understanding type()")
29
30# type() has two uses:
31# 1. Return the type of an object
32# 2. Create a new class dynamically
33
34print("type() as type checker:")
35print(f" type(42) = {type(42)}")
36print(f" type('hello') = {type('hello')}")
37print(f" type([]) = {type([])}")
38
39
40# Create class with type()
41print("\nCreating class with type():")
42
43
44def init_method(self, value):
45 self.value = value
46
47
48def display_method(self):
49 return f"MyClass(value={self.value})"
50
51
52# type(name, bases, dict)
53MyClass = type('MyClass', (object,), {
54 '__init__': init_method,
55 'display': display_method,
56 'class_var': 42
57})
58
59obj = MyClass(100)
60print(f" Created: {obj.display()}")
61print(f" Class var: {MyClass.class_var}")
62print(f" type(MyClass) = {type(MyClass)}")
63
64
65# =============================================================================
66# Basic Metaclass
67# =============================================================================
68
69section("Basic Metaclass")
70
71
72class SimpleMeta(type):
73 """Simple metaclass that prints when class is created."""
74
75 def __new__(mcs, name, bases, namespace, **kwargs):
76 print(f" Creating class '{name}' with SimpleMeta")
77 print(f" Bases: {bases}")
78 print(f" Namespace keys: {list(namespace.keys())}")
79 cls = super().__new__(mcs, name, bases, namespace)
80 return cls
81
82
83class MyClassWithMeta(metaclass=SimpleMeta):
84 """Class using SimpleMeta."""
85
86 class_attr = "I'm a class attribute"
87
88 def instance_method(self):
89 return "I'm an instance method"
90
91
92print("\nInstantiating MyClassWithMeta:")
93instance = MyClassWithMeta()
94print(f" Instance created: {instance}")
95
96
97# =============================================================================
98# Metaclass with __init__
99# =============================================================================
100
101section("Metaclass __init__")
102
103
104class InitMeta(type):
105 """Metaclass with __init__ to modify class after creation."""
106
107 def __init__(cls, name, bases, namespace):
108 super().__init__(name, bases, namespace)
109 # Add attribute to class after creation
110 cls.metaclass_added = f"Added by InitMeta to {name}"
111 print(f" InitMeta.__init__ called for {name}")
112
113
114class MyInitClass(metaclass=InitMeta):
115 """Class using InitMeta."""
116 pass
117
118
119print(f"MyInitClass.metaclass_added = {MyInitClass.metaclass_added}")
120
121
122# =============================================================================
123# Registry Pattern
124# =============================================================================
125
126section("Registry Pattern with Metaclass")
127
128
129class RegistryMeta(type):
130 """Metaclass that maintains a registry of all subclasses."""
131
132 registry: Dict[str, type] = {}
133
134 def __new__(mcs, name, bases, namespace):
135 cls = super().__new__(mcs, name, bases, namespace)
136
137 # Don't register the base class itself
138 if bases:
139 mcs.registry[name] = cls
140 print(f" Registered: {name}")
141
142 return cls
143
144 @classmethod
145 def get_registry(mcs):
146 return mcs.registry.copy()
147
148
149class Plugin(metaclass=RegistryMeta):
150 """Base plugin class."""
151 pass
152
153
154class PluginA(Plugin):
155 """First plugin."""
156 def run(self):
157 return "PluginA running"
158
159
160class PluginB(Plugin):
161 """Second plugin."""
162 def run(self):
163 return "PluginB running"
164
165
166class PluginC(Plugin):
167 """Third plugin."""
168 def run(self):
169 return "PluginC running"
170
171
172print("\nRegistry contents:")
173for name, cls in RegistryMeta.get_registry().items():
174 print(f" {name}: {cls}")
175
176print("\nInstantiating plugins from registry:")
177for name, cls in RegistryMeta.get_registry().items():
178 plugin = cls()
179 print(f" {name}: {plugin.run()}")
180
181
182# =============================================================================
183# Singleton Pattern
184# =============================================================================
185
186section("Singleton Pattern with Metaclass")
187
188
189class SingletonMeta(type):
190 """Metaclass that implements singleton pattern."""
191
192 _instances: Dict[type, Any] = {}
193
194 def __call__(cls, *args, **kwargs):
195 if cls not in cls._instances:
196 print(f" Creating new instance of {cls.__name__}")
197 instance = super().__call__(*args, **kwargs)
198 cls._instances[cls] = instance
199 else:
200 print(f" Returning existing instance of {cls.__name__}")
201
202 return cls._instances[cls]
203
204
205class Database(metaclass=SingletonMeta):
206 """Singleton database connection."""
207
208 def __init__(self, connection_string: str):
209 self.connection_string = connection_string
210 print(f" Database initialized with: {connection_string}")
211
212
213db1 = Database("postgresql://localhost/db1")
214db2 = Database("postgresql://localhost/db2") # Same instance!
215
216print(f"\ndb1 is db2: {db1 is db2}")
217print(f"db1.connection_string: {db1.connection_string}")
218
219
220# =============================================================================
221# Attribute Validation
222# =============================================================================
223
224section("Attribute Validation with Metaclass")
225
226
227class ValidatedMeta(type):
228 """Metaclass that validates class attributes."""
229
230 def __new__(mcs, name, bases, namespace):
231 # Check that required_fields are present
232 if 'required_fields' in namespace:
233 required = namespace['required_fields']
234 for field in required:
235 if field not in namespace:
236 raise TypeError(
237 f"Class {name} missing required field: {field}"
238 )
239
240 return super().__new__(mcs, name, bases, namespace)
241
242
243class ValidModel(metaclass=ValidatedMeta):
244 """Base model with validation."""
245 required_fields = ['name', 'version']
246
247 name = "ValidModel"
248 version = "1.0"
249
250
251print("ValidModel created successfully")
252print(f" name: {ValidModel.name}")
253print(f" version: {ValidModel.version}")
254
255
256# Try creating invalid model
257try:
258 class InvalidModel(metaclass=ValidatedMeta):
259 required_fields = ['name', 'version']
260 name = "InvalidModel"
261 # Missing 'version' field
262except TypeError as e:
263 print(f"\nInvalidModel creation failed: {e}")
264
265
266# =============================================================================
267# __init_subclass__ Hook (Alternative to Metaclass)
268# =============================================================================
269
270section("__init_subclass__ Hook")
271
272
273class PluginBase:
274 """Base class using __init_subclass__ instead of metaclass."""
275
276 plugins = {}
277
278 def __init_subclass__(cls, plugin_name: str = None, **kwargs):
279 super().__init_subclass__(**kwargs)
280 if plugin_name:
281 cls.plugins[plugin_name] = cls
282 print(f" Registered plugin: {plugin_name} -> {cls.__name__}")
283
284
285class ImagePlugin(PluginBase, plugin_name="image"):
286 """Image processing plugin."""
287 def process(self):
288 return "Processing image"
289
290
291class VideoPlugin(PluginBase, plugin_name="video"):
292 """Video processing plugin."""
293 def process(self):
294 return "Processing video"
295
296
297print("\nPlugins registered via __init_subclass__:")
298for name, cls in PluginBase.plugins.items():
299 print(f" {name}: {cls.__name__}")
300
301
302# =============================================================================
303# ABCMeta - Abstract Base Classes
304# =============================================================================
305
306section("ABCMeta - Abstract Base Classes")
307
308
309class Shape(metaclass=ABCMeta):
310 """Abstract shape class."""
311
312 @abstractmethod
313 def area(self) -> float:
314 """Calculate area."""
315 pass
316
317 @abstractmethod
318 def perimeter(self) -> float:
319 """Calculate perimeter."""
320 pass
321
322
323class Rectangle(Shape):
324 """Concrete rectangle implementation."""
325
326 def __init__(self, width: float, height: float):
327 self.width = width
328 self.height = height
329
330 def area(self) -> float:
331 return self.width * self.height
332
333 def perimeter(self) -> float:
334 return 2 * (self.width + self.height)
335
336
337rect = Rectangle(5, 3)
338print(f"Rectangle(5, 3):")
339print(f" Area: {rect.area()}")
340print(f" Perimeter: {rect.perimeter()}")
341
342
343# Try to instantiate abstract class
344try:
345 shape = Shape()
346except TypeError as e:
347 print(f"\nCannot instantiate abstract class:")
348 print(f" {e}")
349
350
351# =============================================================================
352# Metaclass Inheritance
353# =============================================================================
354
355section("Metaclass Inheritance")
356
357
358class MetaA(type):
359 """First metaclass."""
360 def __new__(mcs, name, bases, namespace):
361 print(f" MetaA processing {name}")
362 namespace['from_meta_a'] = True
363 return super().__new__(mcs, name, bases, namespace)
364
365
366class MetaB(MetaA):
367 """Metaclass inheriting from MetaA."""
368 def __new__(mcs, name, bases, namespace):
369 print(f" MetaB processing {name}")
370 namespace['from_meta_b'] = True
371 return super().__new__(mcs, name, bases, namespace)
372
373
374class MyDerivedClass(metaclass=MetaB):
375 """Class using derived metaclass."""
376 pass
377
378
379print(f"\nMyDerivedClass.from_meta_a: {MyDerivedClass.from_meta_a}")
380print(f"MyDerivedClass.from_meta_b: {MyDerivedClass.from_meta_b}")
381
382
383# =============================================================================
384# Summary
385# =============================================================================
386
387section("Summary")
388
389print("""
390Metaclass patterns covered:
3911. type() - the metaclass of all classes
3922. Custom metaclasses - control class creation
3933. Registry pattern - auto-register subclasses
3944. Singleton pattern - single instance enforcement
3955. Validation - ensure class contracts
3966. __init_subclass__ - simpler alternative to metaclasses
3977. ABCMeta - abstract base classes
3988. Metaclass inheritance - compose metaclass behavior
399
400When to use metaclasses:
401- Framework/library development
402- Enforcing API contracts
403- Auto-registration patterns
404- Domain-specific languages
405- ORM implementations
406
407Alternatives to consider:
408- Class decorators (simpler)
409- __init_subclass__ (for registration)
410- Descriptors (for attribute control)
411
412"Metaclasses are deeper magic than 99% of users should ever
413worry about." - Tim Peters
414""")