← 返回首页
🔬

元编程

📂 python ⏱ 4 min 752 words

元编程

元编程是编写操作程序的程序。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))     # 可以哈希

元编程的使用原则

元编程虽然强大,但应谨慎使用:

元编程是Python的高级特性,理解它能让你写出更灵活的代码,但也需要注意保持代码的可读性和可维护性。