Dynamic Class Creation and Modification in Python: Techniques and 10 Practical Examples
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 class structures for various programming scenarios.
Dynamic class creation and modification are important Python metaprogramming techniques that allow developers to 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 modify class behavior dynamically:
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 modify class behavior dynamically:
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
# The following line raises ValueError
# obj2 = MyClass(x=10, y="invalid")6. 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()
execution_time = end_time - start_time
print(f"Method '{method.__name__}' executed in {execution_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 1.000... seconds7. Implementing caching with 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. Using a metaclass for attribute access control:
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
# The following line raises AttributeError
# print(obj._private_attr)9. Implementing logging 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. Implementing interface checking 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
# Instantiating MyClass would raise TypeError because 'method' is abstractThese examples demonstrate how dynamic class creation and modification techniques can make Python code more flexible and maintainable, enabling developers to adapt class structures and behavior at runtime for a wide range of programming challenges.
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.