Python3 中装饰器的用法

1. 什么是装饰器?

装饰器(Decorator)是一种用于修改函数或方法行为的函数,通常用于代码复用、功能增强
它的本质是一个高阶函数(即接受一个函数作为参数并返回一个新函数)。

常见用途:

  • 日志记录
  • 权限验证
  • 执行时间计算
  • 缓存
  • 事务管理
  • Flask 路由装饰器

2. Python 装饰器的基本使用

装饰器的本质是一个返回函数的函数,一般使用 @decorator_name 语法。

(1)基础装饰器

def my_decorator(func):
    def wrapper():
        print("开始执行函数")
        func()
        print("函数执行结束")
    return wrapper

@my_decorator  # 等价于 hello = my_decorator(hello)
def hello():
    print("Hello, World!")

hello()
运行结果
开始执行函数
Hello, World!
函数执行结束

说明

  1. my_decorator 是装饰器,接受 func 作为参数。
  2. wrapper 是内部函数,先执行装饰逻辑(print("开始执行函数")),再执行 func()
  3. @my_decorator 作用于 hello(),相当于 hello = my_decorator(hello)

3. 带参数的装饰器

如果被装饰的函数有参数,装饰器内部的 wrapper 也必须接受参数

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("开始执行函数")
        result = func(*args, **kwargs)
        print("函数执行结束")
        return result
    return wrapper

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

print(add(3, 5))
运行结果
开始执行函数
函数执行结束
8

说明

  • 使用 *args, **kwargs 确保装饰器适用于任意参数的函数。

4. 带参数的装饰器

如果想让装饰器本身也接收参数,需要再封装一层

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

@repeat(3)  # 等价于 hello = repeat(3)(hello)
def hello():
    print("Hello!")

hello()
运行结果
Hello!
Hello!
Hello!

说明

  1. repeat(n) 先接收参数 n,返回 decorator
  2. decorator(func) 处理被装饰的函数,返回 wrapper
  3. @repeat(3) 使 hello() 执行 3 次。

5. functools.wraps 保持原函数信息

被装饰后,原函数的 __name____doc__ 会丢失:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("执行装饰器")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def hello():
    """这是 hello 函数"""
    print("Hello!")

print(hello.__name__)  # wrapper
print(hello.__doc__)   # None
解决方案:使用 functools.wraps
import functools

def my_decorator(func):
    @functools.wraps(func)  # 保持原函数信息
    def wrapper(*args, **kwargs):
        print("执行装饰器")
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def hello():
    """这是 hello 函数"""
    print("Hello!")

print(hello.__name__)  # hello
print(hello.__doc__)   # 这是 hello 函数

结论

  • @functools.wraps(func) 确保 hello() 仍保留自己的 __name____doc__

6. 适用于类的装饰器

装饰器不仅可以作用于函数,也可以作用于

(1)装饰类的所有方法

def class_decorator(cls):
    for attr, value in cls.__dict__.items():
        if callable(value):  # 只装饰方法
            setattr(cls, attr, my_decorator(value))
    return cls

@class_decorator
class MyClass:
    def hello(self):
        print("Hello, World!")

obj = MyClass()
obj.hello()
运行结果
开始执行函数
Hello, World!
函数执行结束

(2)使用 __call__ 作为装饰器

class Timer:
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            import time
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print(f"{func.__name__} 执行时间: {end - start:.4f} 秒")
            return result
        return wrapper

@Timer()  # 这里实例化了 Timer
def slow_function():
    import time
    time.sleep(2)
    print("函数执行完成")

slow_function()
运行结果
函数执行完成
slow_function 执行时间: 2.0003 秒

说明

  • __call__ 使 Timer 类的实例可直接作为装饰器使用。

7. 内置装饰器

Python 内置了一些常见装饰器,如:

  • @staticmethod:定义静态方法,无需 self
  • @classmethod:定义类方法,cls 作为第一个参数。
  • @property:将方法转换为属性。
class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name  # 作为属性访问

    @staticmethod
    def greet():
        print("Hello!")

    @classmethod
    def create(cls, name):
        return cls(name)

p = Person("Alice")
print(p.name)  # 访问属性,不是方法调用
p.greet()      # 静态方法
new_p = Person.create("Bob")  # 类方法

8. 多个装饰器的嵌套

多个装饰器从内向外执行:

def deco1(func):
    def wrapper():
        print("执行 deco1")
        func()
    return wrapper

def deco2(func):
    def wrapper():
        print("执行 deco2")
        func()
    return wrapper

@deco1
@deco2
def hello():
    print("Hello!")

hello()
运行结果
执行 deco1
执行 deco2
Hello!

说明

  1. @deco2 先装饰 hello,然后 @deco1 再装饰 deco2(hello)

9. 总结

特性 说明
基本装饰器 @decorator 语法,包装函数
带参数装饰器 需要再封装一层
functools.wraps 保持原函数信息
作用于类 可以装饰类或类方法
内置装饰器 @staticmethod@classmethod@property
多个装饰器 从内到外执行

装饰器是一种强大的工具,在 Web 开发(如 Flask)、日志、权限控制等方面非常有用!

你可能感兴趣的:(Python基础知识,面试题,python,开发语言)