Chat
Ask me anything
Ithy Logo
Sign in to save

深入理解Python装饰器:定义、使用方法及应用场景

APRENDER A PROGRAMAR CON PYTHON: DECORATORS

什么是Python装饰器

Python的装饰器(Decorator)是一种高级功能,允许开发者在不修改原有函数或类代码的情况下,动态地为其添加额外的功能。装饰器本质上是一个接受函数作为参数并返回一个新函数的高阶函数。通过装饰器,可以实现代码复用、增强代码的可读性和可维护性,避免代码重复。

装饰器的基本语法

定义装饰器

装饰器通常通过定义一个函数来实现,该函数接受一个函数作为参数,并返回一个包装后的新函数。以下是一个基本的装饰器示例:


def my_decorator(func):
    def wrapper():
        print("在函数调用前执行")
        func()
        print("在函数调用后执行")
    return wrapper
  

应用装饰器

使用@符号可以方便地将装饰器应用到目标函数上。上述装饰器可以这样使用:


@my_decorator
def say_hello():
    print("Hello, World!")

say_hello()
  

运行上述代码的输出将是:


在函数调用前执行
Hello, World!
在函数调用后执行
  

带参数的装饰器

装饰器不仅可以装饰不带参数的函数,还可以装饰带有参数的函数。这需要装饰器内部包装函数能够接受任意数量的位置参数和关键字参数。以下是一个带参数的装饰器示例:


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

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

greet("Alice")
  

上述代码将会输出三次问候语:


Hello, Alice!
Hello, Alice!
Hello, Alice!
  

装饰器的使用方法

基本使用方法

装饰器的基础使用涉及两个主要步骤:

  1. 定义装饰器函数:创建一个接受函数作为参数并返回新函数的装饰器。
  2. 应用装饰器:使用@装饰器名称语法将装饰器应用到目标函数。

装饰器链(多个装饰器的叠加)

Python允许多个装饰器叠加使用,从下到上依次应用。例如:


def decorator_one(func):
    def wrapper(*args, **kwargs):
        print("装饰器一执行前")
        result = func(*args, **kwargs)
        print("装饰器一执行后")
        return result
    return wrapper

def decorator_two(func):
    def wrapper(*args, **kwargs):
        print("装饰器二执行前")
        result = func(*args, **kwargs)
        print("装饰器二执行后")
        return result
    return wrapper

@decorator_one
@decorator_two
def say_message():
    print("这是被装饰的函数。")

say_message()
  

运行结果为:


装饰器一执行前
装饰器二执行前
这是被装饰的函数。
装饰器二执行后
装饰器一执行后
  

保持原函数元数据

使用装饰器时,原函数的元数据(如函数名、文档字符串等)可能会被包装函数覆盖。为了解决这个问题,可以使用functools.wraps装饰器:


import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("装饰器执行前")
        result = func(*args, **kwargs)
        print("装饰器执行后")
        return result
    return wrapper

@my_decorator
def say_hello():
    """打印问候语"""
    print("Hello!")

print(say_hello.__name__)  # 输出: say_hello
print(say_hello.__doc__)   # 输出: 打印问候语
  

通过@functools.wraps(func),包装函数wrapper将继承被装饰函数的元数据。

装饰器的常见应用场景

1. 日志记录

通过装饰器,可以在函数调用前后记录日志信息,便于调试和监控系统行为。


def logger(func):
    import functools
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        print(f"参数: args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"返回结果: {result}")
        return result
    return wrapper

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

add(1, 2)
  

运行结果:


调用函数: add
参数: args=(1, 2), kwargs={}
返回结果: 3
  

2. 性能分析

通过装饰器,可以测量函数的执行时间,有助于性能优化。


import time
import functools

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数 {func.__name__} 执行时间:{end_time - start_time:.4f} 秒")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(2)
    print("执行完成")

slow_function()
  

运行结果:


执行完成
函数 slow_function 执行时间:2.0021 秒
  

3. 权限控制

装饰器可以用于检查用户权限,确保只有具有特定权限的用户才能执行某些功能。


def permission_required(permission):
    def decorator(func):
        import functools
        @functools.wraps(func)
        def wrapper(user, *args, **kwargs):
            if user.has_permission(permission):
                return func(user, *args, **kwargs)
            else:
                raise PermissionError("没有权限执行此操作")
        return wrapper
    return decorator

class User:
    def __init__(self, permissions):
        self.permissions = permissions
    
    def has_permission(self, perm):
        return perm in self.permissions

@permission_required('admin')
def delete_user(user, user_id):
    print(f"用户 {user_id} 已被删除。")

admin_user = User(['admin', 'editor'])
regular_user = User(['viewer'])

delete_user(admin_user, 123)      # 成功执行
delete_user(regular_user, 456)    # 抛出PermissionError
  

4. 缓存

装饰器可以用于缓存函数的返回结果,以减少重复计算,提升性能。


import functools
import time

def cache_result(ttl=60):
    cache = {}
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            key = (args, tuple(sorted(kwargs.items())))
            current_time = time.time()
            if key in cache:
                result, timestamp = cache[key]
                if current_time - timestamp < ttl:
                    print("从缓存获取结果")
                    return result
            result = func(*args, **kwargs)
            cache[key] = (result, current_time)
            return result
        return wrapper
    return decorator

@cache_result(ttl=10)
def compute_sum(a, b):
    time.sleep(1)  # 模拟耗时操作
    return a + b

print(compute_sum(1, 2))  # 计算并缓存
print(compute_sum(1, 2))  # 从缓存获取
  

运行结果:


3
从缓存获取结果
3
  

5. 输入输出验证

装饰器可以用于验证函数的输入参数和输出结果,确保数据的正确性和一致性。


def validate_types(*type_args, **type_kwargs):
    def decorator(func):
        import functools
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for (a, t) in zip(args, type_args):
                if not isinstance(a, t):
                    raise TypeError(f"参数 {a} 不是类型 {t}")
            for key, t in type_kwargs.items():
                if key in kwargs and not isinstance(kwargs[key], t):
                    raise TypeError(f"参数 {key} 不是类型 {t}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@validate_types(int, int)
def add(a, b):
    return a + b

print(add(1, 2))    # 正常执行
print(add(1, '2'))  # 抛出TypeError
  

6. 事务处理

在数据库操作或其他需要事务管理的场景中,装饰器可以确保事务的一致性。


def transaction(func):
    import functools
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            print("事务开始")
            result = func(*args, **kwargs)
            print("事务提交")
            return result
        except Exception as e:
            print("事务回滚")
            raise e
    return wrapper

@transaction
def update_database(record):
    if not record:
        raise ValueError("记录不能为空")
    print(f"更新记录: {record}")

update_database({"id": 1, "value": "新的值"})
update_database(None)  # 将触发回滚
  

运行结果:


事务开始
更新记录: {'id': 1, 'value': '新的值'}
事务提交
事务开始
事务回滚
Traceback (most recent call last):
    ...
ValueError: 记录不能为空
  

装饰器的高级用法

带参数的装饰器

装饰器本身可以接受参数,这需要在装饰器外层再包裹一层函数来接收这些参数。


def decorator_with_args(arg1, arg2):
    def decorator(func):
        import functools
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print(f"装饰器参数: {arg1}, {arg2}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@decorator_with_args("参数1", "参数2")
def say(message):
    print(message)

say("你好,世界!")
  

运行结果:


装饰器参数: 参数1, 参数2
你好,世界!
  

类装饰器

除了函数,装饰器也可以用于类,通过修改类的行为或属性。


def class_decorator(cls):
    cls.extra_attribute = "这是一个额外的属性"
    return cls

@class_decorator
class MyClass:
    def __init__(self, name):
        self.name = name

obj = MyClass("测试")
print(obj.name)               # 输出: 测试
print(obj.extra_attribute)    # 输出: 这是一个额外的属性
  

装饰器与原型继承的结合

在复杂的类层次结构中,装饰器可以与原型继承结合使用,以动态地修改或扩展类的功能。


def add_method(cls):
    def new_method(self):
        return f"{self.name} 调用了新方法"
    cls.new_method = new_method
    return cls

@add_method
class Person:
    def __init__(self, name):
        self.name = name

p = Person("Alice")
print(p.new_method())  # 输出: Alice 调用了新方法
  

装饰器的最佳实践与注意事项

使用functools.wraps保持元数据

在定义装饰器时,使用@functools.wraps可以确保装饰后的函数保留原函数的元数据,如名称和文档字符串。

避免装饰器引起的命名冲突

如果多个装饰器修改了同一函数的同一部分,可能会导致命名冲突或功能覆盖。因此,应谨慎设计装饰器的功能和顺序。

避免过度使用装饰器

虽然装饰器功能强大,但过度使用可能会导致代码难以理解和维护。因此,应在适当的场景下使用装饰器,确保代码的可读性和简洁性。

性能考虑

每添加一个装饰器,函数调用将多一层包装,这可能会对性能产生一定影响。对于性能要求高的场景,应评估装饰器带来的开销。

调试装饰器

由于装饰器会修改函数的行为,调试时可能会增加复杂度。建议在开发过程中,逐步添加装饰器,并做好日志记录,以便追踪问题。

内置装饰器与常用装饰器

@property

@property装饰器用于将类的方法转换为属性,提供了对属性的访问控制。


class Person:
    def __init__(self, name):
        self._name = name
    
    @property
    def name(self):
        return self._name
    
    @name.setter
    def name(self, value):
        if not value:
            raise ValueError("名字不能为空")
        self._name = value

p = Person("Bob")
print(p.name)  # 输出: Bob
p.name = "Alice"
print(p.name)  # 输出: Alice
  

@classmethod 与 @staticmethod

@classmethod和@staticmethod是用于定义类方法和静态方法的装饰器。


class MyClass:
    class_variable = 0

    @classmethod
    def increment_class_variable(cls):
        cls.class_variable += 1
    
    @staticmethod
    def static_method():
        print("这是一个静态方法")

MyClass.increment_class_variable()
print(MyClass.class_variable)  # 输出: 1
MyClass.static_method()         # 输出: 这是一个静态方法
  

装饰器的进一步学习资源

要深入学习Python装饰器,可以参考以下资源:

总结

Python装饰器是一种强大的语法特性,能够帮助开发者以简洁、优雅的方式为函数或类添加额外的功能。通过装饰器,可以实现日志记录、性能分析、权限控制、缓存、事务处理等多种功能,显著提升代码的可读性和可维护性。在掌握装饰器的基本用法后,深入理解其高级用法和最佳实践,将有助于编写更加高效、模块化的Python代码。


Last updated January 7, 2025

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI
Download Article
Delete Article