自动化测试(五)python-装饰器

99 阅读1分钟

装饰器通过 @decorator_name 语法糖实现,常用于增强功能(如日志、权限校验)而不修改原代码。

1. 函数装饰器(无参数)

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"开始执行: {func.__name__}")
        result = func(*args, **kwargs)  # 执行原函数
        print(f"执行结束: {func.__name__}")
        return result
    return wrapper

@logger
def add(a, b):
    return a + b

add(3, 5)  # 输出执行日志

2. 函数装饰器(带参数)

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"Hello {name}!")

greet("Alice")  # 打印 3 次 Hello Alice!

3. 类装饰器

class Timer:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        import time
        start = time.time()
        result = self.func(*args, **kwargs)
        print(f"耗时: {time.time() - start:.2f}s")
        return result

@Timer
def heavy_task():
    time.sleep(2)

heavy_task()  # 输出执行时间

装饰器的实战应用

1. 单例模式装饰器

def singleton(cls):
    instances = {}
    @wraps(cls)
    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrapper

@singleton
class Database:
    def __init__(self):
        print("数据库连接已建立")

db1 = Database()  # 输出 "数据库连接已建立"
db2 = Database()  # 无输出
print(db1 is db2)  # True

2. 权限校验

def login_required(func):
    @wraps(func)
    def wrapper(user, *args, **kwargs):
        if not user.is_authenticated:
            raise PermissionError("需要登录")
        return func(user, *args, **kwargs)
    return wrapper

@login_required
def profile(user):
    print(f"用户主页: {user.name}")

3. 性能分析

def benchmark(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        import time
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__} 耗时: {time.time() - start:.4f}s")
        return result
    return wrapper

装饰器的注意事项

  1. 不要过度使用:装饰器会增加代码复杂度
  2. 处理参数:始终用 *args, **kwargs 接收参数
  3. 调试问题:使用 @wraps 保留原函数信息
  4. 线程安全:装饰器内部状态需考虑线程安全(如用锁)