Metaprogramming - CameronAuler/python-devops GitHub Wiki
Metaprogramming is a technique where code modifies itself or manipulates classes dynamically at runtime. Python supports metaprogramming through metaclasses, dynamic class creation, and the type()
function.
Table of Contents
- Metaclasses
- Dynamic Classes
- Using
type()
for Introspection & Modification - Combining Metaclasses &
type()
Metaclasses
A metaclass is a class that defines the behavior of other classes. Normally, Python classes are created using class
, but metaclasses allow modifying class creation dynamically. Classes in Python are instances of a metaclass (default is type
).
How Classes Work
In the example below, Foo
is a class, but it is also an instance of type
, which is the default metaclass. A metaclass controls how classes are created, just as classes control how objects are created.
class Foo:
pass
print(type(Foo)) # <class 'type'> (Foo is an instance of 'type')
# Output:
<class 'type'>
Creating a Custom Metaclass
Metaclasses define __new__()
or __init__()
to modify class creation. Meta
is a metaclass (inherits from type
). When MyClass
is defined, Meta.__new__()
is called, modifying its creation.
class Meta(type): # Inherit from 'type' to create a metaclass
def __new__(cls, name, bases, class_dict):
print(f"Creating class: {name}")
return super().__new__(cls, name, bases, class_dict)
class MyClass(metaclass=Meta): # Assign Meta as metaclass
pass
# Output:
Creating class: MyClass
Dynamic Classes
Creating Classes at Runtime
Classes can be created dynamically using type()
, which is Python’s built-in metaclass. type(name, bases, dict)
creates a class dynamically.
name
: Class name (MyDynamicClass
).bases
: Parent classes (()
means no parent).dict
: Class attributes ({"x": 42}
adds an attributex
).
MyDynamicClass = type("MyDynamicClass", (), {"x": 42})
print(MyDynamicClass) # <class '__main__.MyDynamicClass'>
print(MyDynamicClass().x) # 42
# Output:
<class '__main__.MyDynamicClass'>
42
Adding Methods Dynamically
Functions can be added dynamically when creating classes. This is mainly used for generating classes at runtime, useful in ORMs, frameworks, and dynamic APIs.
def hello(self):
return "Hello from dynamic class!"
DynamicClass = type("DynamicClass", (), {"greet": hello})
obj = DynamicClass()
print(obj.greet()) # Calls the dynamically added method
# Output:
Hello from dynamic class!
type()
for Introspection & Modification
Using The type()
function is used for checking the type of an object and Creating new classes dynamically (as seen above).
Checking Object Type
This is mainly used for type checking at runtime.
print(type(42)) # <class 'int'>
print(type("Hello")) # <class 'str'>
print(type([])) # <class 'list'>
# Output:
<class 'int'>
<class 'str'>
<class 'list'>
Modifying an Existing Class
New attributes can be added dynamically using type()
. It is mainly used for monkey patching, testing, debugging, and dynamic behavior changes.
class Person:
pass
Person.age = 30 # Dynamically add an attribute
print(Person.age) # 30
# Output:
30
type()
Combining Metaclasses & We can use type()
inside a metaclass to generate classes dynamically. Meta.__new__()
modifies the class before it’s created. dynamic_attribute
is added dynamically during class creation.
class Meta(type):
def __new__(cls, name, bases, class_dict):
print(f"Creating class dynamically: {name}")
class_dict["dynamic_attribute"] = 100 # Add an attribute
return type.__new__(cls, name, bases, class_dict)
class DynamicClass(metaclass=Meta):
pass
print(DynamicClass.dynamic_attribute) # 100
# Output:
Creating class dynamically: DynamicClass
100