Python - Metaprogramming in Python (Reflection, Introspection, and Dynamic Code)
Metaprogramming in Python refers to the ability of a program to examine, modify, or even generate code at runtime. In simple terms, it means writing code that can manipulate other code or itself. Python supports metaprogramming naturally because it treats everything—functions, classes, modules—as objects that can be inspected and modified dynamically.
1. Introspection (Examining Code at Runtime)
Introspection is the ability to inspect the structure and properties of objects during execution. Python provides several built-in functions for this purpose:
-
type()– returns the type of an object -
id()– returns the unique identity of an object -
dir()– lists all attributes and methods of an object -
getattr()– retrieves an attribute dynamically -
hasattr()– checks if an attribute exists
Example:
class Student:
def __init__(self, name):
self.name = name
s = Student("Teena")
print(type(s)) # <class '__main__.Student'>
print(dir(s)) # Shows all attributes and methods
print(hasattr(s, 'name')) # True
print(getattr(s, 'name')) # Teena
This allows programs to adapt behavior based on object structure at runtime.
2. Reflection (Modifying Code Dynamically)
Reflection goes beyond inspection and allows modification of objects during runtime. You can dynamically add, update, or delete attributes and methods.
Example:
class Employee:
pass
e = Employee()
# Adding attribute dynamically
setattr(e, 'salary', 50000)
print(e.salary) # 50000
# Modifying attribute
setattr(e, 'salary', 60000)
# Removing attribute
delattr(e, 'salary')
This is useful in frameworks and dynamic systems where structure is not fixed beforehand.
3. Dynamic Function and Class Creation
Python allows creating functions and classes dynamically using built-in functions like type().
Example:
# Creating a class dynamically
Person = type('Person', (), {'name': 'Teena', 'age': 25})
p = Person()
print(p.name) # Teena
Here:
-
'Person'is the class name -
()represents base classes -
{}contains attributes
This technique is widely used in frameworks and ORMs.
4. Metaclasses (Controlling Class Creation)
Metaclasses are one of the most advanced metaprogramming concepts. A metaclass defines how a class behaves. Just like classes create objects, metaclasses create classes.
Example:
class Meta(type):
def __new__(cls, name, bases, dct):
dct['company'] = 'ABC Corp'
return super().__new__(cls, name, bases, dct)
class Developer(metaclass=Meta):
pass
d = Developer()
print(d.company) # ABC Corp
Here, the metaclass automatically adds a property to the class during its creation.
Metaclasses are used in large frameworks like Django to enforce rules and automate class behavior.
5. Code Generation Using exec() and eval()
Python allows execution of dynamically created code using:
-
eval()– evaluates expressions -
exec()– executes statements
Example:
code = "x = 10\ny = 20\nprint(x + y)"
exec(code)
This executes the code string at runtime. While powerful, it must be used carefully due to security risks.
6. Decorators as a Form of Metaprogramming
Decorators modify the behavior of functions or methods without changing their actual code.
Example:
def logger(func):
def wrapper():
print("Function is being called")
func()
return wrapper
@logger
def greet():
print("Hello")
greet()
Decorators dynamically wrap functions, making them a practical metaprogramming tool.
7. Real-World Applications
Metaprogramming is widely used in:
-
Frameworks like Django and Flask for routing and model definitions
-
ORMs for mapping database tables to Python classes
-
Serialization libraries (like JSON or Pydantic)
-
Plugin systems where functionality is loaded dynamically
-
Testing frameworks that automatically discover test cases
8. Advantages and Limitations
Advantages:
-
Highly flexible and dynamic code
-
Reduces repetitive code
-
Enables powerful abstractions
Limitations:
-
Makes code harder to read and debug
-
Can introduce runtime errors instead of compile-time errors
-
Overuse can reduce maintainability
Conclusion
Metaprogramming in Python is a powerful concept that allows developers to build dynamic and adaptable systems. By using introspection, reflection, dynamic class creation, and metaclasses, Python programs can modify their own behavior at runtime. While it offers great flexibility, it should be used carefully to maintain clarity and reliability in code.