Python装饰器使用

目录

介绍

🧠 什么是 Python 装饰器?

装饰器(Decorator) 是一个函数,它可以在不修改原函数代码的前提下,增强或修改函数的行为。本质上,装饰器就是“函数的函数”。

它常用于:

• 代码复用(比如日志、性能统计、权限校验)

• AOP 编程思想(面向切面编程)

💡 常见应用场景

  1. 日志记录

  2. 性能测试

  3. 权限验证

  4. 缓存(如 LRU 缓存)

  5. 重试机制

  6. Flask、Django 等框架中的路由注册、权限控制等

🧰 Python 内置装饰器示例

• @staticmethod

• @classmethod

• @property

• @functools.lru_cache

🧪 基本语法

def decorator(func):
    def wrapper(*args, **kwargs): # wrapper 是包装函数
        print("调用前")
        result = func(*args, **kwargs)
        print("调用后")
        return result
    return wrapper # 返回包装后的函数

@decorator
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

这个装饰器 等价于
def say_hello(name):
    print(f"Hello, {name}")

say_hello = decorator(say_hello)

>>>>>>>>>>>
调用前
Hello, Alice!
调用后

func只是 decorator(func) 里传进来的一个函数参数,你可以叫它 f、target_func、banana 都可以(当然为了可读性我们通常叫它 func 或 fn)。

重点:

原始函数 say_hello --> 被装饰器 decorator 包装 --> 得到新函数 wrapper -->  wrapper 替换原函数

执行阶段

是先执行装饰器,还是里面的函数?

在程序加载时,先执行装饰器(也就是装饰器函数 decorator(func)),

而在程序运行时,才执行被包裹的原始函数。

🧱 阶段一:定义阶段(加载 Python 文件时)

@decorator
def greet(name):
    print(f"Hi, {name}")

其实它做的事情是:

# 把 greet 函数对象传进去,执行 decorator 函数
greet = decorator(greet)

这一步是 立即执行 decorator 函数,并返回一个 wrapper 函数,再赋值回 greet。

此时函数体还没有执行,只是函数被“装饰”了,变成了 wrapper。

def decorator(func):
    print("装饰器被执行!")  # 加载阶段运行
    def wrapper(*args, **kwargs):
        print("调用前")
        result = func(*args, **kwargs)
        print("调用后")
        return result
    return wrapper

@decorator
def greet(name):
    print(f"Hi, {name}")

print("准备调用 greet 函数了")
greet("Tom")

>>>>>>>>>>
装饰器被执行
准备调用 greet 函数了
调用前
Hi, Tom
调用后
时机 发生什么
Python 加载函数时 执行装饰器 decorator(func),返回 wrapper
调用函数时 执行 wrapper(*args, **kwargs),间接调用原始 func()

进阶用法

多个装饰器

@decorator1
@decorator2
def my_func():
    ...

等价于:

my_func = decorator1(decorator2(my_func))

🛠 带参数的装饰器

def log(prefix):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"{prefix} 调用 {func.__name__}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log("DEBUG")
def run():
    print("Running...")

run()

同步&异步装饰器

import asyncio
import functools

def async_compatible_decorator(func):
    @functools.wraps(func)
    def sync_wrapper(*args, **kwargs):
        print("装饰器:调用前")

        if asyncio.iscoroutinefunction(func):
            # 如果是异步函数,返回一个协程函数
            async def async_inner():
                result = await func(*args, **kwargs)
                print("装饰器:调用后")
                return result
            return async_inner()
        else:
            # 同步函数
            result = func(*args, **kwargs)
            print("装饰器:调用后")
            return result

    return sync_wrapper

测试使用用法:

@async_compatible_decorator
def say_hi(name):
    print(f"Hi {name}")

@async_compatible_decorator
async def say_hi_async(name):
    print(f"Hi async {name}")

say_hi("Tom")  # 同步调用

asyncio.run(say_hi_async("Alice"))  # 异步调用

🔍 为什么要这么写?

关键点 解释
asyncio.iscoroutinefunction(func) 判断函数是不是 async def
async def async_inner(): await func(…) 正确调用异步函数
同步函数走正常流程 兼容 def 写法
functools.wraps(func) 保留原函数信息,避免装饰器污染函数名/文档

🚨一个同步的装饰器(def decorator(func)),怎么能装饰一个 async def 异步函数呢?

装饰器本身是同步的,它只在函数定义时执行

而你真正要“同步 or 异步”,其实是你包装返回的 wrapper 函数的执行阶段

这时候 my_decorator 是一个同步函数,它接受了一个异步函数对象作为参数,然后返回一个新函数(通常叫 wrapper)。

✅ 装饰器本身是同步的,这没问题,因为它只是拿到函数对象,并返回另一个函数而已。

问题 解释
装饰器是同步的,可以装饰异步函数吗? 可以,因为它只是返回包装函数,不执行原函数
同步装饰器包装异步函数安全吗? 不安全,除非你返回的是 async def wrapper() 并正确 await
解决方法 判断是否 async,再返回不同类型的 wrapper

点赞一下

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦