Python 装饰器 decorator

1. 什么是装饰器

装饰器(Decorator)是 Python 中一种非常优雅的语法糖,它允许你在不修改原函数代码的前提下,为函数动态增加额外的功能。简单来说,装饰器就是一个接收函数作为参数、并返回一个新函数的高阶函数。

装饰器最常见的应用场景包括:日志记录、性能计时、权限校验、缓存结果等。它让代码更加简洁、可复用,是 Python 进阶路上必须掌握的核心技能之一。

2. 函数是一等公民

在理解装饰器之前,需要先明确一个 Python 的重要特性:函数是一等公民。这意味着函数可以像普通变量一样被赋值、作为参数传递、作为返回值返回。

python 复制代码
def greet(name):
    return f"Hello, {name}!"

# 函数可以赋值给变量
say_hello = greet
print(say_hello("Alice"))  # 输出: Hello, Alice!

# 函数可以作为参数传递
def call_func(func, arg):
    return func(arg)

print(call_func(greet, "Bob"))  # 输出: Hello, Bob!

正是因为函数可以像普通对象一样被传递和返回,装饰器才有了实现的基础。

3. 自己写装饰器 使用 @ 语法糖

上面的例子中,say_hello 没有参数。如果被装饰的函数需要接收参数,wrapper 必须使用 *args 和 **kwargs 来接收任意数量的位置参数和关键字参数:

python 复制代码
def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}, 参数: {args}, {kwargs}")
        result = func(*args, **kwargs)
        print(f"函数返回: {result}")
        return result
    return wrapper

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

print(add(3, 5))

运行结果:

复制代码
调用函数: add, 参数: (3, 5), {}
函数返回: 8
8

使用 *args 和 **kwargs 后,装饰器可以适用于任意签名的函数,通用性大大增强。

4. 带参数的装饰器

有时我们希望装饰器本身也能接收参数,比如指定日志级别。这时需要在装饰器外层再包一层函数:

python 复制代码
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!

这里 repeat(3) 先执行,返回真正的装饰器 decorator,再由 decorator 装饰 greet 函数。三层嵌套结构是带参数装饰器的标准写法。

5. langchain 中的 tool 装饰器

定义

python 复制代码
import inspect
from dataclasses import dataclass
from typing import Callable


@dataclass
class Tool:
    name: str
    description: str
    func: Callable
    args_schema: dict

    def invoke(self, *args, **kwargs):
        return self.func(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        return self.invoke(*args, **kwargs)


def tool(func: Callable) -> Tool:
    """把普通函数包装成 Tool 对象"""
    sig = inspect.signature(func)
    args_schema = {
        name: param.annotation.__name__ if param.annotation != inspect.Parameter.empty else "Any"
        for name, param in sig.parameters.items()
    }

    return Tool(
        name=func.__name__,
        description=(func.__doc__ or "").strip(),
        func=func,
        args_schema=args_schema,
    )

用例

python 复制代码
@tool
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b


print(add.name)          # add
print(add.description)   # Add two numbers.
print(add.args_schema)   # {'a': 'int', 'b': 'int'}
print(add.invoke(1, 2))  # 3

6. 总结

装饰器是 Python 中非常强大的特性,核心要点可以归纳为:

  • 本质:装饰器是一个接收函数、返回新函数的高阶函数。
  • 语法糖 :@decorator 等价于 func = decorator(func)。
  • 通用性 :使用 *args 和 **kwargs 让装饰器适配任意函数签名。
  • 元信息 :使用 functools.wraps 保留原函数的名称和文档。
  • 执行顺序:多个装饰器从下往上应用,从上往下调用。
  • 实现方式:既可以用函数实现,也可以用类实现。

掌握装饰器后,你可以写出更加简洁、优雅且可复用的 Python 代码。建议在实际项目中多尝试用装饰器抽取公共逻辑,体会它带来的便利。

相关推荐
菜鸟~noob2336 小时前
【电子战】第三季预告—波武器与电子系统强力毁伤
开发语言·数据库·matlab·电子战
ShyanZh6 小时前
【Python3基础】19-Socket 与 TCP、UDP 编程
python·网络协议·tcp/ip·udp
惊讶的猫7 小时前
工具选对之后还不够:商城 Agent 的参数与权限治理
开发语言·python
cmpxr_7 小时前
【MicroPython】如何刷新ESP8266的固件
开发语言·micropython
天赐范式7 小时前
天赐范式第186天:让方差开始自洽——Bulmer效应的闭式验证
python·数字生命·天赐范式·动态运行时·遗传方差·bulmer效应
泡茶喝茶写代码7 小时前
A股量化数据工程:从 REST 接口到策略信号(第 8 篇):成长能力因子:营收与利润增速
java·python·股票数据api·股票数据api接口·股票量化数据api·股票量化数据接口·股票数据api数据
搭贝7 小时前
上厕所的时间搭好一套系统:AI自动生成实测
开发语言·人工智能·低代码·ai·php·搭贝
智感子7 小时前
智能称重·数据上云:从仪表读数到可追溯的计量凭证
开发语言·php
\光辉岁月/7 小时前
7.javase-面向对象
java·开发语言
Είναι η κοπέλα8 小时前
显存计算与模型选择:你的显卡能跑多大的模型
人工智能·pytorch·python·开源·conda