Dynamic Class Creation and Modification in Python: 10 Practical Scenarios
This article explains Python's dynamic class creation and modification techniques—including the type function, metaclasses, and class decorators—through ten practical code examples that demonstrate how to build flexible, maintainable, and powerful object‑oriented designs.
Dynamic class creation and modification are key metaprogramming techniques in Python that let developers define and alter class structures at runtime using the type function, metaclasses, and class decorators.
1. Using the type function to create a class dynamically:
MyClass = type("MyClass", (object,), {"x": 42})
obj = MyClass()
print(obj.x) # 输出: 422. Using a metaclass to inject attributes into a class:
class MyMeta(type):
def __new__(cls, name, bases, attrs):
attrs["y"] = 100
return super().__new__(cls, name, bases, attrs)
class MyClass(metaclass=MyMeta):
pass
obj = MyClass()
print(obj.y) # 输出: 1003. Using a class decorator to add attributes:
def my_decorator(cls):
cls.z = 200
return cls
@my_decorator
class MyClass:
pass
obj = MyClass()
print(obj.z) # 输出: 2004. Implementing the Singleton pattern with a metaclass:
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class SingletonClass(metaclass=SingletonMeta):
pass
obj1 = SingletonClass()
obj2 = SingletonClass()
print(obj1 is obj2) # 输出: True5. Using a class decorator for attribute validation:
def validate_attributes(cls):
original_init = cls.__init__
def new_init(self, *args, **kwargs):
for name, value in kwargs.items():
if not isinstance(value, int):
raise ValueError(f"Invalid value for attribute '{name}'")
original_init(self, *args, **kwargs)
cls.__init__ = new_init
return cls
@validate_attributes
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
obj = MyClass(x=10, y=20)
print(obj.x, obj.y) # 输出: 10 20
obj2 = MyClass(x=10, y="invalid") # 抛出 ValueError6. Implementing a method timer with a metaclass:
import time
class TimerMeta(type):
def __new__(cls, name, bases, attrs):
for attr, value in attrs.items():
if callable(value):
attrs[attr] = cls.wrap_method(value)
return super().__new__(cls, name, bases, attrs)
@staticmethod
def wrap_method(method):
def wrapper(*args, **kwargs):
start_time = time.time()
result = method(*args, **kwargs)
end_time = time.time()
print(f"Method '{method.__name__}' executed in {end_time - start_time} seconds")
return result
return wrapper
class MyClass(metaclass=TimerMeta):
def my_method(self):
time.sleep(1)
obj = MyClass()
obj.my_method() # 输出: Method 'my_method' executed in ... seconds7. Adding caching to a method via a class decorator:
def cache_result(cls):
cache = {}
original_method = cls.method
def new_method(self, *args):
if args in cache:
return cache[args]
result = original_method(self, *args)
cache[args] = result
return result
cls.method = new_method
return cls
@cache_result
class MyClass:
def method(self, x):
print(f"Calculating result for {x}")
return x * 2
obj = MyClass()
print(obj.method(5)) # 输出: Calculating result for 5, 10
print(obj.method(5)) # 输出: 10 (从缓存中获取)8. Controlling attribute access with a metaclass:
class AccessControlMeta(type):
def __new__(cls, name, bases, attrs):
attrs["_private_attr"] = 42
return super().__new__(cls, name, bases, attrs)
def __getattr__(cls, name):
if name == "public_attr":
return cls._private_attr
raise AttributeError(f"Attribute '{name}' not found")
class MyClass(metaclass=AccessControlMeta):
pass
obj = MyClass()
print(obj.public_attr) # 输出: 42
print(obj._private_attr) # 抛出 AttributeError9. Logging method calls with a class decorator:
def log_calls(cls):
original_method = cls.method
def new_method(self, *args, **kwargs):
print(f"Calling method '{method.__name__}' with args={args}, kwargs={kwargs}")
return original_method(self, *args, **kwargs)
cls.method = new_method
return cls
@log_calls
class MyClass:
def method(self, x):
return x * 2
obj = MyClass()
obj.method(5) # 输出: Calling method 'method' with args=(5,), kwargs={}10. Enforcing interface contracts with a metaclass:
class InterfaceMeta(type):
def __new__(cls, name, bases, attrs):
if "__abstractmethods__" not in attrs:
abstract_methods = set()
for base in bases:
if hasattr(base, "__abstractmethods__"):
abstract_methods.update(base.__abstractmethods__)
for attr, value in attrs.items():
if callable(value) and getattr(value, "__isabstractmethod__", False):
abstract_methods.add(attr)
attrs["__abstractmethods__"] = frozenset(abstract_methods)
return super().__new__(cls, name, bases, attrs)
class MyInterface(metaclass=InterfaceMeta):
def method(self):
raise NotImplementedError
class MyClass(MyInterface):
pass
# 抛出 TypeError: Can't instantiate abstract class MyClass with abstract methods methodThese ten examples demonstrate how Python's metaprogramming facilities can be leveraged to create, modify, and extend classes dynamically, resulting in more flexible and maintainable code.
Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.