元编程
元编程
元编程是编写操作程序的程序。Python作为动态语言,提供了强大的元编程能力,让你可以在运行时修改类、函数和代码本身。
元类基础
元类是类的类。类定义了类的实例的行为,而元类定义了类的行为。
# 所有类都是type的实例
class MyClass:
pass
obj = MyClass()
print(type(obj)) # <class '__main__.MyClass'>
print(type(MyClass)) # <class 'type'>
print(type(type)) # <class 'type'>
# 使用type创建类
MyClass = type('MyClass', (object,), {'x': 10, 'greet': lambda self: "Hello"})
obj = MyClass()
print(obj.x) # 10
print(obj.greet()) # Hello
自定义元类
class SingletonMeta(type):
"""单例元类"""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self):
self.connection = "已连接"
db1 = Database()
db2 = Database()
print(db1 is db2) # True
验证属性的元类
class ValidatedMeta(type):
"""验证属性类型的元类"""
def __new__(mcs, name, bases, namespace):
annotations = namespace.get('__annotations__', {})
for attr_name, attr_type in annotations.items():
if attr_name.startswith('_'):
continue
original = namespace.get(attr_name)
def make_validator(attr_name, attr_type, original):
def validator(self, value):
if not isinstance(value, attr_type):
raise TypeError(
f"属性 {attr_name} 必须是 {attr_type.__name__},"
f"而不是 {type(value).__name__}"
)
return value
return validator
validator = make_validator(attr_name, attr_type, original)
namespace[attr_name] = property(
fget=lambda self, n=attr_name: getattr(self, f'_{n}'),
fset=lambda self, value, n=attr_name, v=validator:
object.__setattr__(self, f'_{n}', v(self, value))
)
return super().__new__(mcs, name, bases, namespace)
class Person(metaclass=ValidatedMeta):
name: str
age: int
def __init__(self, name: str, age: int):
self.name = name
self.age = age
p = Person("张三", 25)
print(p.name, p.age)
try:
p.age = "二十五" # TypeError: 属性 age 必须是 int
except TypeError as e:
print(e)
__new__方法
__new__是一个静态方法,在__init__之前被调用,用于创建实例。
class ImmutablePoint:
"""不可变点类"""
def __new__(cls, x: int, y: int):
instance = super().__new__(cls)
object.__setattr__(instance, '_x', x)
object.__setattr__(instance, '_y', y)
return instance
@property
def x(self):
return self._x
@property
def y(self):
return self._y
def __setattr__(self, name, value):
raise AttributeError("ImmutablePoint是不可变的")
def __repr__(self):
return f"ImmutablePoint({self._x}, {self._y})"
p = ImmutablePoint(1, 2)
print(p) # ImmutablePoint(1, 2)
try:
p.x = 5 # AttributeError
except AttributeError as e:
print(e)
工厂模式用__new__
class Shape:
"""形状基类"""
def __new__(cls, *args, **kwargs):
if cls is Shape:
shape_type = kwargs.get('type', args[0] if args else None)
if shape_type == 'circle':
return Circle(*args[1:], **{k:v for k,v in kwargs.items() if k != 'type'})
elif shape_type == 'rectangle':
return Rectangle(*args[1:], **{k:v for k,v in kwargs.items() if k != 'type'})
return super().__new__(cls)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
# 工厂调用
circle = Shape(type='circle', radius=5)
rect = Shape(type='rectangle', width=4, height=6)
print(f"圆形面积: {circle.area()}") # 圆形面积: 78.5
print(f"矩形面积: {rect.area()}") # 矩形面积: 24
exec和eval
exec执行动态代码,eval计算表达式。
# eval执行表达式
result = eval('2 + 3 * 4')
print(result) # 14
# eval可以访问局部和全局变量
x = 10
result = eval('x * 2 + 1')
print(result) # 21
# exec执行代码块
code = """
class Calculator:
def __init__(self):
self.history = []
def add(self, a, b):
result = a + b
self.history.append(f'{a} + {b} = {result}')
return result
def multiply(self, a, b):
result = a * b
self.history.append(f'{a} * {b} = {result}')
return result
"""
exec(code)
calc = Calculator()
print(calc.add(2, 3)) # 5
print(calc.multiply(4, 5)) # 20
print(calc.history) # ['2 + 3 = 5', '4 * 5 = 20']
安全执行环境
def safe_execute(code: str, allowed_builtins: list = None):
"""安全执行代码,限制可用的内置函数"""
safe_builtins = {
'print': print,
'range': range,
'len': len,
'int': int,
'float': float,
'str': str,
'list': list,
'dict': dict,
'True': True,
'False': False,
'None': None,
}
if allowed_builtins:
for name in allowed_builtins:
if name in __builtins__:
safe_builtins[name] = __builtins__[name] if isinstance(__builtins__, dict)
else getattr(__builtins__, name)
namespace = {'__builtins__': safe_builtins}
exec(code, namespace)
return namespace
# 安全执行
safe_code = """
result = []
for i in range(10):
if i % 2 == 0:
result.append(i ** 2)
print(result)
"""
safe_execute(safe_code) # [0, 4, 16, 36, 64]
描述符简介
描述符是实现了特定协议的对象,用于自定义属性访问。
class Validator:
"""验证描述符"""
def __init__(self, min_value=None, max_value=None):
self.min_value = min_value
self.max_value = max_value
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
return getattr(obj, f'_{self.name}', None)
def __set__(self, obj, value):
if self.min_value is not None and value < self.min_value:
raise ValueError(f"{self.name} 不能小于 {self.min_value}")
if self.max_value is not None and value > self.max_value:
raise ValueError(f"{self.name} 不能大于 {self.max_value}")
setattr(obj, f'_{self.name}', value)
class Student:
name = Validator()
score = Validator(min_value=0, max_value=100)
age = Validator(min_value=0, max_value=150)
def __init__(self, name, score, age):
self.name = name
self.score = score
self.age = age
s = Student("张三", 85, 20)
print(f"{s.name}, 成绩: {s.score}")
try:
s.score = 150 # ValueError
except ValueError as e:
print(e) # score 不能大于 100
类装饰器
类装饰器比元类更简单,适用于大多数场景。
from functools import wraps
def add_repr(cls):
"""添加__repr__方法"""
def __repr__(self):
attrs = ', '.join(f'{k}={v!r}' for k, v in self.__dict__.items())
return f'{cls.__name__}({attrs})'
cls.__repr__ = __repr__
return cls
def add_eq(cls):
"""添加__eq__和__hash__方法"""
def __eq__(self, other):
if type(self) != type(other):
return False
return self.__dict__ == other.__dict__
def __hash__(self):
return hash(tuple(sorted(self.__dict__.items())))
cls.__eq__ = __eq__
cls.__hash__ = __hash__
return cls
@add_repr
@add_eq
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)
print(p1) # Point(x=1, y=2)
print(p1 == p2) # True
print(p1 == p3) # False
print(hash(p1)) # 可以哈希
元编程的使用原则
元编程虽然强大,但应谨慎使用:
- 优先使用装饰器:装饰器比元类更简单,适用于大多数场景
- 元类用于框架开发:Django ORM、SQLAlchemy等框架使用元类
- 避免过度使用:元编程代码难以理解和调试
- 文档化你的元编程:确保其他开发者能理解你的意图
元编程是Python的高级特性,理解它能让你写出更灵活的代码,但也需要注意保持代码的可读性和可维护性。