Python 进阶教程:装饰器与元类

前言

装饰器(Decorator)和元类(Metaclass)是 Python 最强大的两个”语法糖”。装饰器让我们在不修改原函数/类的前提下,动态地增强其行为;元类则让我们在类创建时介入,控制整个类的生成过程。

它们是 Python 中面向切面编程黑魔法的代表——用得恰当可以让代码优雅简洁,用得过度则会让代码难以理解。

本文将系统讲解:

  • 装饰器:从闭包到函数装饰器,再到类装饰器
  • 元类:类的创建过程与元类的介入机制
  • 实战场景:缓存、日志、性能分析、注册模式等
  • 最佳实践:何时用装饰器,何时用元类

函数基础:闭包

什么是闭包

# 闭包 = 函数 + 外部作用域的变量引用
def outer(x):
    def inner(y):
        return x + y  # 引用外部变量 x
    return inner
​
closure = outer(10)
print(closure(5))   # 15
print(closure(3))   # 13
​
# inner 函数"记住"了 x=10,即使 outer 已经返回

闭包的经典应用:计数器

def make_counter():
    count = 0  # 外部变量
    
    def counter():
        nonlocal count  # 声明使用外部变量
        count += 1
        return count
    
    return counter
​
counter1 = make_counter()
counter2 = make_counter()
​
print(counter1())  # 1
print(counter1())  # 2
print(counter2())  # 1(独立的作用域)
print(counter1())  # 3

闭包 vs 普通函数

特性普通函数闭包
状态保持无状态可保持外部状态
内存函数结束时释放引用存在则不释放
用途一次性计算工厂函数、回调
性能轻量略重,但功能强大

装饰器基础

装饰器是什么

# 装饰器本质:高阶函数,接受函数作为参数,返回增强后的函数
​
# 原始函数
def original_function():
    return "原始功能"
​
# 装饰器
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("调用前增强")
        result = func(*args, **kwargs)
        print("调用后增强")
        return result
    return wrapper
​
# 使用 @ 语法糖
@my_decorator
def original_function():
    return "原始功能"
​
# 等价于
# original_function = my_decorator(original_function)
​
# 调用
result = original_function()
# 输出:
# 调用前增强
# 调用后增强
# result = "原始功能"

无参数函数的装饰器

import time
​
def timer(func):
    """计时装饰器"""
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} 执行耗时: {elapsed:.4f}s")
        return result
    return wrapper
​
@timer
def slow_function():
    time.sleep(1)
    return "完成"
​
@timer
def fast_function():
    return "完成"
​
slow_function()
# 输出: slow_function 执行耗时: 1.0032s
​
fast_function()
# 输出: fast_function 执行耗时: 0.0001s

*args 和 **kwargs 的作用

def debug(func):
    """调试装饰器:打印函数调用信息"""
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__}")
        print(f"  位置参数: {args}")
        print(f"  关键字参数: {kwargs}")
        
        result = func(*args, **kwargs)
        
        print(f"  返回值: {result}")
        return result
    return wrapper
​
@debug
def add(a, b):
    return a + b
​
@debug
def greet(name, prefix="Hello"):
    return f"{prefix}, {name}!"
​
add(1, 2)
# 输出:
# 调用 add
#   位置参数: (1, 2)
#   关键字参数: {}
#   返回值: 3
​
greet("Alice", prefix="Hi")
# 输出:
# 调用 greet
#   位置参数: ('Alice',)
#   关键字参数: {'prefix': 'Hi'}
#   返回值: Hi, Alice!

带参数的装饰器

双层嵌套:装饰器工厂

# 单参数装饰器
def repeat(times):
    """重复执行函数指定次数"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args, **kwargs))
            return results
        return wrapper
    return decorator
​
@repeat(3)
def say_hello():
    return "Hello!"
​
print(say_hello())  # ['Hello!', 'Hello!', 'Hello!']
​
@repeat(2)
def add(a, b):
    return a + b
​
print(add(1, 2))  # [3, 3]

多参数装饰器工厂

def repeat(times=None, delay=0):
    """重复执行,可选延迟"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            import time
            results = []
            for i in range(times or 1):
                result = func(*args, **kwargs)
                results.append(result)
                if i < times - 1 and delay:
                    time.sleep(delay)
            return results
        return wrapper
    return decorator
​
@repeat(times=3, delay=1)
def fetch_data():
    return "数据"
​
# 执行 fetch_data 将重复3次,每次间隔1秒

装饰器带参数的三种写法

# 写法1:@repeat(3) — 推荐
@repeat(3)
def func1():
    pass
​
# 写法2:@repeat() — 无参数
@repeat()
def func2():
    pass
​
# 写法3:@repeat — 不带括号(需要装饰器支持)
# 注意:这要求装饰器本身可调用
def simple_decorator(func):
    # ...
​
@simple_decorator  # 等价于 @simple_decorator
def func3():
    pass

装饰器组合

def deco1(func):
    def wrapper(*args, **kwargs):
        print("装饰器1开始")
        result = func(*args, **kwargs)
        print("装饰器1结束")
        return result
    return wrapper
​
def deco2(func):
    def wrapper(*args, **kwargs):
        print("装饰器2开始")
        result = func(*args, **kwargs)
        print("装饰器2结束")
        return result
    return wrapper
​
# 装饰器从下往上应用
@deco1
@deco2
def my_func():
    print("执行函数")
​
my_func()
# 输出顺序:
# 装饰器1开始
# 装饰器2开始
# 执行函数
# 装饰器2结束
# 装饰器1结束

类装饰器

类装饰器基础

# 类装饰器:接受类作为参数,返回类(或修改类)
​
def add_method(cls):
    """为类添加方法"""
    def new_method(self):
        return "新增方法"
    cls.new_method = new_method
    return cls
​
@add_method
class MyClass:
    def original(self):
        return "原有方法"
​
obj = MyClass()
print(obj.original())  # "原有方法"
print(obj.new_method())  # "新增方法"

常用类装饰器模式

# 模式1:注册器(Registry)
class_registry = {}
​
def register(cls):
    """注册类到全局注册表"""
    class_registry[cls.__name__] = cls
    return cls
​
@register
class User:
    pass
​
@register
class Product:
    pass
​
print(class_registry)  # {'User': <class '__main__.User'>, 'Product': <class '__main__.Product'>}
​
# 模式2:添加类方法
def class_method(*method_names):
    """批量添加类方法"""
    def decorator(cls):
        for name in method_names:
            setattr(cls, name, classmethod(lambda cls: f"类方法: {name}"))
        return cls
    return decorator
​
@class_method("create", "destroy")
class Service:
    pass
​
print(Service.create())  # "类方法: create"
print(Service.destroy())  # "类方法: destroy"
​
# 模式3:实例计数
def count_instances(cls):
    """统计类实例数量"""
    cls._count = 0
    original_init = cls.__init__
    
    def new_init(self, *args, **kwargs):
        cls._count += 1
        original_init(self, *args, **kwargs)
    
    cls.__init__ = new_init
    return cls
​
@count_instances
class Tracked:
    pass
​
a = Tracked()
b = Tracked()
c = Tracked()
print(Tracked._count)  # 3

类方法 vs 静态方法装饰器

class MyClass:
    def instance_method(self):
        """实例方法:需要 self"""
        return "实例方法"
    
    @classmethod
    def class_method(cls):
        """类方法:需要 cls"""
        return f"类方法 via {cls.__name__}"
    
    @staticmethod
    def static_method():
        """静态方法:不需要 self 或 cls"""
        return "静态方法"
​
obj = MyClass()
​
print(obj.instance_method())   # "实例方法"
print(MyClass.class_method()) # "类方法 via MyClass"
print(MyClass.static_method()) # "静态方法"
print(obj.static_method())    # "静态方法"(也可用实例调用)
​
# 在装饰器中保留这些特性
def add_class_methods(methods_dict):
    """动态添加类方法和静态方法"""
    def decorator(cls):
        for name, method in methods_dict.items():
            if isinstance(method, staticmethod):
                setattr(cls, name, method.__func__)
            elif isinstance(method, classmethod):
                setattr(cls, name, method.__func__)
            else:
                setattr(cls, name, staticmethod(method))
        return cls
    return decorator

functools 工具箱

@functools.wraps — 保留元信息

import functools
​
def my_decorator(func):
    @functools.wraps(func)  # 关键!保留原函数信息
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
​
@my_decorator
def documented_function():
    """这是一个有文档字符串的函数"""
    pass
​
print(documented_function.__name__)  # "documented_function"(而非 "wrapper")
print(documented_function.__doc__)  # "这是一个有文档字符串的函数"
print(documented_function.__module__)  # "__main__"
​
# 不使用 @wraps 的问题
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper
​
@bad_decorator
def another_function():
    """文档"""
    pass
​
print(another_function.__name__)  # "wrapper" ← 丢失!
print(another_function.__doc__)   # None ← 丢失!

functools.lru_cache — 缓存结果

import functools
import time
​
@functools.lru_cache(maxsize=128)
def fibonacci(n):
    """斐波那契数列(带缓存)"""
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
​
# 性能对比
def fibonacci_slow(n):
    """不使用缓存"""
    if n < 2:
        return n
    return fibonacci_slow(n-1) + fibonacci_slow(n-2)
​
# 测试
start = time.time()
result = fibonacci_slow(35)
print(f"无缓存: {time.time() - start:.4f}s")  # ~1.5s
​
start = time.time()
result = fibonacci(35)
print(f"有缓存: {time.time() - start:.4f}s")  # ~0.0001s
​
# 查看缓存状态
print(fibonacci.cache_info())
# CacheInfo(hits=33, misses=36, maxsize=128, currsize=36)

functools.partial — 偏函数

import functools
​
def power(base, exponent):
    return base ** exponent
​
# 创建偏函数:固定 exponent=2
square = functools.partial(power, exponent=2)
print(square(5))  # 25
print(square(10)) # 100
​
# 创建偏函数:固定 base=2
cube = functools.partial(power, 2)  # 位置参数
print(cube(3))  # 8
print(cube(4))  # 16
​
# 实际应用:日志记录器
def log(level, message):
    print(f"[{level}] {message}")
​
debug_log = functools.partial(log, "DEBUG")
info_log = functools.partial(log, "INFO")
​
debug_log("调试信息")  # [DEBUG] 调试信息
info_log("普通信息")   # [INFO] 普通信息

functools.singledispatch — 泛型函数

import functools
​
@functools.singledispatch
def serialize(obj):
    """默认序列化"""
    return str(obj)
​
@serialize.register(int)
def _(n):
    return f"整数: {n}"
​
@serialize.register(str)
def _(s):
    return f"字符串: {s}"
​
@serialize.register(list)
def _(lst):
    return f"列表: {', '.join(str(x) for x in lst)}"
​
print(serialize(42))           # "整数: 42"
print(serialize("hello"))      # "字符串: hello"
print(serialize([1, 2, 3]))    # "列表: 1, 2, 3"
print(serialize({'a': 1}))     # "字典: {'a': 1}"

实战场景

场景1:日志记录装饰器

import functools
import logging
from datetime import datetime

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def log_calls(logger_func=logger.info):
    """日志装饰器"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start = datetime.now()
            logger_func(f"调用 {func.__name__},参数: args={args}, kwargs={kwargs}")
            try:
                result = func(*args, **kwargs)
                elapsed = (datetime.now() - start).total_seconds()
                logger_func(f"{func.__name__} 成功,耗时 {elapsed:.3f}s,返回: {result}")
                return result
            except Exception as e:
                elapsed = (datetime.now() - start).total_seconds()
                logger_func(f"{func.__name__} 失败,耗时 {elapsed:.3f}s,异常: {e}")
                raise
        return wrapper
    return decorator

@log_calls()
def risky_operation(x):
    if x < 0:
        raise ValueError("不能是负数")
    return x * 2

@log_calls(logger_func=logging.debug)
def debug_operation():
    return "调试信息"

场景2:重试装饰器

import functools
import time
import logging
​
def retry(max_attempts=3, delay=1, backoff=2, exceptions=(Exception,)):
    """
    重试装饰器
    
    Args:
        max_attempts: 最大尝试次数
        delay: 初始延迟(秒)
        backoff: 退避倍数
        exceptions: 需要重试的异常类型元组
    """
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            current_delay = delay
            last_exception = None
            
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    if attempt == max_attempts:
                        logging.error(f"{func.__name__} 重试 {max_attempts} 次后仍失败")
                        raise
                    
                    logging.warning(
                        f"{func.__name__} 第 {attempt} 次失败: {e},"
                        f"{current_delay}s 后重试..."
                    )
                    time.sleep(current_delay)
                    current_delay *= backoff
            
            raise last_exception
        return wrapper
    return decorator
​
@retry(max_attempts=3, delay=1, exceptions=(ConnectionError, TimeoutError))
def fetch_from_api(url):
    """模拟不稳定的 API 调用"""
    import random
    if random.random() < 0.7:  # 70% 概率失败
        raise ConnectionError("网络连接失败")
    return "API 响应数据"

场景3:性能分析装饰器

import functools
import time
import statistics
​
class PerformanceMonitor:
    """性能监控器"""
    _metrics = {}
    
    @classmethod
    def record(cls, func_name, duration):
        if func_name not in cls._metrics:
            cls._metrics[func_name] = []
        cls._metrics[func_name].append(duration)
    
    @classmethod
    def report(cls):
        print("\n性能报告:")
        print("-" * 50)
        for func, durations in cls._metrics.items():
            print(f"{func}:")
            print(f"  调用次数: {len(durations)}")
            print(f"  平均耗时: {statistics.mean(durations):.4f}s")
            print(f"  最短耗时: {min(durations):.4f}s")
            print(f"  最长耗时: {max(durations):.4f}s")
            if len(durations) > 1:
                print(f"  标准差:   {statistics.stdev(durations):.4f}s")
​
def monitor(func):
    """性能监控装饰器"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        try:
            result = func(*args, **kwargs)
            return result
        finally:
            duration = time.perf_counter() - start
            PerformanceMonitor.record(func.__name__, duration)
    return wrapper
​
@monitor
def matrix_multiply(a, b):
    """矩阵乘法(示例)"""
    time.sleep(0.1)  # 模拟计算
    return [[sum(x*y for x,y in zip(row,col)) for col in zip(*b)] for row in a]
​
# 使用
matrix_multiply([[1,2],[3,4]], [[5,6],[7,8]])
matrix_multiply([[1,2],[3,4]], [[5,6],[7,8]])
matrix_multiply([[1,2],[3,4]], [[5,6],[7,8]])
​
PerformanceMonitor.report()

场景4:参数验证装饰器

import functools
​
def validate(**validators):
    """
    参数验证装饰器工厂
    
    Args:
        validators: {参数名: 验证函数} 或 {参数名: (验证函数, 错误消息)}
    """
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # 合并位置参数和关键字参数
            import inspect
            sig = inspect.signature(func)
            bound = sig.bind(*args, **kwargs)
            bound.apply_defaults()
            
            # 验证每个参数
            for param_name, validator in validators.items():
                value = bound.arguments.get(param_name)
                
                # 支持 (验证器, 消息) 格式
                if isinstance(validator, tuple):
                    validator_func, error_msg = validator
                else:
                    validator_func = validator
                    error_msg = f"参数 {param_name} 验证失败"
                
                if not validator_func(value):
                    raise ValueError(f"{error_msg}: {value}")
            
            return func(*args, **kwargs)
        return wrapper
    return decorator
​
def in_range(min_v, max_v):
    return lambda x: min_v <= x <= max_v
​
@validate(
    age=in_range(0, 150),
    name=lambda n: isinstance(n, str) and len(n) > 0,
    score=(lambda s: 0 <= s <= 100, "分数必须在 0-100 之间")
)
def register(name, age, score):
    return f"注册成功: {name}, {age}岁, 分数 {score}"
​
# 测试
try:
    register("Alice", 25, 85)
    print("✅ 验证通过")
except ValueError as e:
    print(f"❌ {e}")
​
try:
    register("", 25, 85)  # name 验证失败
except ValueError as e:
    print(f"❌ {e}")

场景5:缓存装饰器(自定义)

import functools
import hashlib
import pickle
​
def memoize(func):
    """自定义缓存装饰器"""
    cache = {}
    
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # 构建缓存键
        key = (
            args,
            tuple(sorted(kwargs.items()))
        )
        
        if key not in cache:
            cache[key] = func(*args, **kwargs)
        
        return cache[key]
    
    # 添加缓存管理方法
    wrapper.cache = cache
    wrapper.clear_cache = lambda: cache.clear()
    return wrapper
​
@memoize
def expensive_computation(n):
    """耗时计算"""
    return sum(i * i for i in range(n))
​
print(expensive_computation(100000))
print(f"缓存条目: {len(expensive_computation.cache)}")
​
# 清除缓存
expensive_computation.clear_cache()

元类基础

什么是元类

# 类是对象的模板,元类是类的模板
# type 既是类,也是元类
​
# 普通类
class MyClass:
    pass
​
print(MyClass.__class__)  # <class 'type'> — 类默认由 type 创建
​
# type 创建的类
MyClass2 = type('MyClass2', (), {})
print(MyClass2.__class__)  # <class 'type'>
​
# 自定义元类
class Meta(type):
    def __new__(mcs, name, bases, namespace):
        # mcs: 元类本身(Meta)
        # name: 类名(字符串)
        # bases: 父类元组
        # namespace: 类的属性字典
        print(f"创建类: {name}")
        return super().__new__(mcs, name, bases, namespace)
​
class MyClass(metaclass=Meta):  # 指定元类
    pass
​
# 输出: 创建类: MyClass

元类的 new vs init

class Meta(type):
    """元类示例"""
    
    def __new__(mcs, name, bases, namespace):
        """创建类对象时被调用"""
        print(f"__new__: 创建类 {name}")
        # 可以修改 namespace
        namespace['created_by'] = 'Meta'
        return super().__new__(mcs, name, bases, namespace)
    
    def __init__(cls, name, bases, namespace):
        """初始化类对象时被调用"""
        print(f"__init__: 初始化类 {name}")
        super().__init__(name, bases, namespace)
​
class MyClass(metaclass=Meta):
    class_attr = "属性"
​
# 输出:
# __new__: 创建类 MyClass
# __init__: 初始化类 MyClass

元类继承链

class BaseMeta(type):
    def __new__(mcs, name, bases, namespace):
        return super().__new__(mcs, name, bases, namespace)
​
class ChildMeta(BaseMeta):  # 元类也可以继承
    pass
​
class MyClass(metaclass=ChildMeta):
    pass
​
# 元类继承链: ChildMeta -> BaseMeta -> type
print(ChildMeta.__mro__)  # (<class '__main__.ChildMeta'>, <class '__main__.BaseMeta'>, <class 'type'>, <class 'object'>)

元类实战

实战1:自动注册类

class Registry(type):
    """注册器元类:自动将类注册到注册表"""
    _registry = {}
    
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        
        # 如果类有 name 属性,使用它作为注册键
        if 'name' in namespace:
            mcs._registry[namespace['name']] = cls
        else:
            mcs._registry[name] = cls
        
        return cls
    
    @classmethod
    def get(mcs, name):
        return mcs._registry.get(name)
    
    @classmethod
    def list_all(mcs):
        return list(mcs._registry.keys())
​
# 使用元类
class Animal(metaclass=Registry):
    pass
​
class Dog(Animal):
    name = 'dog'
    def bark(self):
        return "汪汪"
​
class Cat(Animal):
    name = 'cat'
    def meow(self):
        return "喵喵"
​
# 获取注册的类
print(Registry.list_all())  # ['Dog', 'Cat']
Dog_class = Registry.get('dog')
print(Dog_class)  # <class '__main__.Dog'>
print(Dog_class().bark())  # "汪汪"

实战2:ORM 字段定义

class Field:
    """字段基类"""
    def __init__(self, field_type, null=True, default=None):
        self.field_type = field_type
        self.null = null
        self.default = default
​
class CharField(Field):
    def __init__(self, max_length=255, **kwargs):
        super().__init__('VARCHAR', **kwargs)
        self.max_length = max_length
​
class IntField(Field):
    def __init__(self, **kwargs):
        super().__init__('INTEGER', **kwargs)
​
class ModelMeta(type):
    """模型元类:自动收集字段"""
    
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        
        # 收集所有 Field 属性
        cls._fields = {}
        for attr_name, attr_value in namespace.items():
            if isinstance(attr_value, Field):
                attr_value.name = attr_name
                cls._fields[attr_name] = attr_value
        
        return cls
​
class Model(metaclass=ModelMeta):
    """模型基类"""
    _fields = {}
​
class User(Model):
    name = CharField(max_length=100, null=False)
    age = IntField(default=0)
    email = CharField(max_length=255)
​
# 检查字段
print("User 字段:")
for name, field in User._fields.items():
    print(f"  {name}: {field.field_type}({field.max_length or 'N/A'})")
​
# 生成的 SQL(示例)
def generate_sql(cls):
    columns = []
    for name, field in cls._fields.items():
        col_def = f"{name} {field.field_type}"
        if isinstance(field, CharField):
            col_def += f"({field.max_length})"
        if not field.null:
            col_def += " NOT NULL"
        if field.default is not None:
            col_def += f" DEFAULT {field.default}"
        columns.append(col_def)
    return f"CREATE TABLE {cls.__name__} ({', '.join(columns)});"
​
print(generate_sql(User))
# CREATE TABLE User (name VARCHAR(100) NOT NULL, age INTEGER DEFAULT 0, email VARCHAR(255));

实战3:单例模式

# 方法1:元类实现单例
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 Database(metaclass=SingletonMeta):
    def __init__(self):
        print("数据库连接已建立")
    
    def query(self, sql):
        return f"执行: {sql}"
​
db1 = Database()
db2 = Database()
print(db1 is db2)  # True — 同一个实例
​
# 方法2:装饰器实现单例
def singleton(cls):
    instances = {}
    
    @functools.wraps(cls)
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    
    return get_instance
​
@singleton
class Config:
    def __init__(self):
        self.settings = {}
    
    def get(self, key):
        return self.settings.get(key)
​
config1 = Config()
config2 = Config()
print(config1 is config2)  # True

实战4:接口强制实现

class InterfaceMeta(type):
    """接口检查元类"""
    
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        
        # 基接口不需要检查
        if not bases or bases == (object,):
            return cls
        
        # 检查是否实现了所有抽象方法
        for base in bases:
            if hasattr(base, '_abstract_methods'):
                for method in base._abstract_methods:
                    if method not in namespace or not callable(namespace[method]):
                        raise TypeError(
                            f"类 {name} 必须实现抽象方法 {method}"
                        )
        
        return cls
​
def abstract_method(func):
    """抽象方法装饰器"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        raise NotImplementedError(f"方法 {func.__name__} 未实现")
    return wrapper
​
class IUserRepository(metaclass=InterfaceMeta):
    """用户仓储接口"""
    _abstract_methods = {'get_by_id', 'save', 'delete'}
​
# 正确实现
class UserRepository(IUserRepository):
    def get_by_id(self, id):
        return {"id": id, "name": "User"}
    
    def save(self, user):
        print(f"保存用户: {user}")
    
    def delete(self, id):
        print(f"删除用户: {id}")
​
# 错误实现(会报错)
try:
    class BadRepository(IUserRepository):
        def get_by_id(self, id):
            return {}
        # 缺少 save 和 delete
        pass
    
    print("不会执行到这里")
except TypeError as e:
    print(f"错误: {e}")

装饰器 vs 元类

对比表

特性装饰器元类
作用对象函数、类仅类
注入时机调用时(运行时)定义时(类创建时)
灵活性更灵活,参数化控制更强大,控制类创建
复杂度简单直观复杂难懂
继承不影响继承继承的类也受元类控制
使用频率非常常见较少使用

选型指南

# ✅ 用装饰器的场景
# 1. 为函数/方法添加横切关注点(日志、缓存、计时)
# 2. 参数验证和预处理
# 3. 运行时修改函数行为
# 4. 需要参数化控制
​
# ✅ 用元类的场景
# 1. 在类定义时修改类结构
# 2. 自动注册类
# 3. 确保接口实现
# 4. ORM 模型的字段收集
# 5. 需要所有子类都受影响
​
# ❌ 避免元类的情况
# 1. 装饰器能完成时不用元类
# 2. 项目其他人不熟悉元类
# 3. 需要频繁调试的场景

替代方案:类组合

# 很多时候组合优于继承,元类也可以用装饰器替代
​
# 元类方案
class AutoPropertyMeta(type):
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        # 自动将 _xxx 属性转换为 @property
        for attr_name in list(namespace.keys()):
            if attr_name.startswith('_') and not attr_name.startswith('__'):
                public_name = attr_name[1:]
                if public_name not in namespace:
                    value = namespace.pop(attr_name)
                    namespace[public_name] = property(lambda self: value)
        return cls
​
# 装饰器替代方案
def auto_property(cls):
    """类装饰器版本的自动 property"""
    for attr_name in list(vars(cls).keys()):
        if attr_name.startswith('_') and not attr_name.startswith('__'):
            public_name = attr_name[1:]
            if public_name not in cls.__dict__:
                value = getattr(cls, attr_name)
                delattr(cls, attr_name)
                setattr(cls, public_name, property(lambda self, v=value: v))
    return cls
​
@auto_property
class Config:
    _debug = True
    _version = "1.0"
​
print(Config().debug)  # True
print(Config().version)  # "1.0"

排错指南

常见错误与解决方案

错误1:装饰器顺序不对

def deco1(func):
    def wrapper(*args):
        print("deco1")
        return func(*args)
    return wrapper
​
def deco2(func):
    def wrapper(*args):
        print("deco2")
        return func(*args)
    return wrapper
​
# ❌ 装饰器顺序影响输出
@deco1
@deco2
def test():
    print("test")
​
test()
# 输出:
# deco1
# deco2
# test
​
# ✅ 从下往上理解:先执行 @deco2,再执行 @deco1

错误2:装饰器丢失元信息

import functools
​
# ❌ 不使用 wraps
def bad_decorator(func):
    def wrapper(*args):
        return func(*args)
    return wrapper
​
@bad_decorator
def my_func():
    """文档"""
    pass
​
print(my_func.__name__)  # "wrapper" ← 错误!
​
# ✅ 使用 wraps
def good_decorator(func):
    @functools.wraps(func)  # 保留元信息
    def wrapper(*args):
        return func(*args)
    return wrapper
​
@good_decorator
def my_func2():
    """文档"""
    pass
​
print(my_func2.__name__)  # "my_func2" ← 正确!

错误3:元类继承问题

class MetaA(type):
    def __new__(mcs, name, bases, namespace):
        print(f"MetaA 创建 {name}")
        return super().__new__(mcs, name, bases, namespace)
​
class MetaB(MetaA):  # MetaB 继承 MetaA
    def __new__(mcs, name, bases, namespace):
        print(f"MetaB 创建 {name}")
        return super().__new__(mcs, name, bases, namespace)
​
# ❌ 问题:子类使用父元类
class MyClass(metaclass=MetaB):
    pass
​
# 输出:
# MetaB 创建 MyClass
# MetaA 创建 MyClass(通过 super() 调用)

错误4:装饰器中的变量作用域

def outer():
    funcs = []
    for i in range(3):
        # ❌ 闭包陷阱:所有函数共享同一个 i
        funcs.append(lambda: i)
    
    return funcs
​
# ❌ 所有函数返回值都是 2
for f in outer():
    print(f(), end=" ")  # 2 2 2
​
# ✅ 修复:使用默认参数捕获值
def outer_fixed():
    funcs = []
    for i in range(3):
        funcs.append(lambda i=i: i)  # i=i 捕获当前值
    
    return funcs
​
for f in outer_fixed():
    print(f(), end=" ")  # 0 1 2

错误5:装饰器在类方法上的行为

class MyClass:
    def __init__(self):
        self.value = 0
    
    @property
    def doubled(self):
        return self.value * 2
    
    # ❌ 装饰器可能破坏 property
    def old_decorator(func):
        def wrapper(self):
            return func(self) * 2
        return wrapper
    
    @old_decorator
    @property
    def value_trippled(self):
        return self.value
​
# 问题:@property 返回的是描述符,不是可调用的函数
​
# ✅ 正确:装饰器需要处理描述符
def smart_decorator(func):
    if isinstance(func, property):
        return property(
            lambda self: func.fget(self) * 2  # 包装 getter
        )
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

总结

核心要点

  1. 装饰器是函数:接受函数/类,返回增强后的函数/类
  2. 闭包是基础:装饰器利用闭包捕获外部状态
  3. @wraps 必不可少:保留原函数的 __name____doc__ 等元信息
  4. 元类控制类创建:在 __new__ 中修改类定义
  5. 装饰器优先:能用装饰器就别用元类

一图总结

┌─────────────────────────────────────────────────────────────────┐
│                    装饰器 vs 元类                               │
├───────────────────────┬─────────────────────────────────────────┤
│        装饰器          │                  元类                   │
├───────────────────────┼─────────────────────────────────────────┤
│ @decorator             │ class Foo(metaclass=Meta):             │
│ def func():            │     pass                                │
│     pass               │                                         │
├───────────────────────┼─────────────────────────────────────────┤
│ 运行时修改             │ 定义时修改                              │
│ 函数/类                │ 仅类                                    │
│ 简单直观               │ 复杂强大                                │
│ 非常常用               │ 谨慎使用                                │
└───────────────────────┴─────────────────────────────────────────┘

常用装饰器模式

# 日志
def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        logging.info(f"调用 {func.__name__}")
        return func(*args, **kwargs)
    return wrapper
​
# 计时
def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__}: {time.time() - start:.4f}s")
        return result
    return wrapper
​
# 重试
def retry(times=3, delay=1):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                try:
                    return func(*args, **kwargs)
                except:
                    time.sleep(delay)
            raise
        return wrapper
    return decorator
​
# 缓存
def cache(func):
    @functools.wraps(func)
    def wrapper(*args):
        if args not in wrapper.cache:
            wrapper.cache[args] = func(*args)
        return wrapper.cache[args]
    wrapper.cache = {}
    return wrapper

💡 提示: 元类和装饰器都是高级特性,优先考虑更简单的方案(如普通函数继承、组合模式)。只有在确实需要横切关注点或元编程时才使用。 📚 扩展阅读:


原创内容,版权所有。未经授权,禁止转载。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容