你好,我是 shengjk1,多年大厂经验,努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注!你会有如下收益:
- 了解大厂经验
- 拥有和大厂相匹配的技术等
希望看什么,评论或者私信告诉我!
一、背景
最近趁着有时间,搞了一下 MCP,MCP server 中定义的 Function ,想要被 Client 使用,必须添加 @mcp.tool()。这其实是 python 的装饰器
二、python 中常见的装饰器的使用方式
Python装饰器是一种动态增强函数或类功能的高阶编程工具,其核心思想是通过包装函数或类来实现代码复用。以下是装饰器的主要类型及实现方式,结合代码示例详细说明:
一、基础装饰器
实现原理:通过嵌套函数包裹原函数,在调用前后添加额外逻辑。
代码示例:
def simple_decorator(func):
def wrapper(*args, **kwargs):
print("函数执行前")
result = func(*args, **kwargs)
print("函数执行后")
return result
return wrapper
@simple_decorator
def say_hello(name):
print(f"Hello, {name}!")
say_hello("Alice")
输出:
函数执行前
Hello, Alice!
函数执行后
二、带参数的装饰器
实现原理:通过三层嵌套函数,外层接收装饰器参数,中间层接收原函数,内层实现逻辑。
代码示例:
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hi, {name}!")
greet("Bob")
输出:
Hi, Bob!
Hi, Bob!
Hi, Bob!
三、类装饰器
实现原理:通过类的 __call__
方法让实例可调用,并维护装饰器状态。
代码示例:
class CallCounter:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"调用次数:{self.calls}")
return self.func(*args, **kwargs)
@CallCounter
def multiply(a, b):
return a * b
print(multiply(2, 3)) # 调用次数:1 → 6
print(multiply(4, 5)) # 调用次数:2 → 20
四、装饰器堆叠
执行顺序:装饰器按从下到上的顺序应用,执行时外层先执行。
代码示例:
def decorator1(func):
def wrapper():
print("装饰器1前置")
func()
print("装饰器1后置")
return wrapper
def decorator2(func):
def wrapper():
print("装饰器2前置")
func()
print("装饰器2后置")
return wrapper
@decorator1
@decorator2
def target():
print("目标函数")
target()
输出:
装饰器1前置
装饰器2前置
目标函数
装饰器2后置
装饰器1后置
五、保留元信息的装饰器
问题:装饰器会覆盖原函数的 __name__
等元信息。
解决方案:使用 functools.wraps
装饰器。
代码示例:
from functools import wraps
def log_decorator(func):
@wraps(func) # 保留原函数元信息
def wrapper(*args, **kwargs):
print(f"调用函数:{func.__name__}")
return func(*args, **kwargs)
return wrapper
@log_decorator
def calculate(a, b):
"""加法运算"""
return a + b
print(calculate.__name__) # 输出:calculate
print(calculate.__doc__) # 输出:加法运算
六、内置装饰器
Python内置的三个常用装饰器:
@staticmethod
:定义静态方法,无需实例化对象即可调用。@classmethod
:定义类方法,首个参数为类本身(cls
)。@property
:将方法转为属性访问。
class MyClass:
@staticmethod
def static_method():
print("静态方法")
@classmethod
def class_method(cls):
print(f"类方法,类名:{cls.__name__}")
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
七、实际应用场景
- 性能测试:
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
duration = time.perf_counter() - start
print(f"{func.__name__} 耗时:{duration:.4f}秒")
return result
return wrapper
@timer
def complex_calculation():
time.sleep(1.5)
- 权限校验:
def admin_required(func):
def wrapper(user, *args, **kwargs):
if user.role != 'admin':
raise PermissionError("需要管理员权限")
return func(user, *args, **kwargs)
return wrapper
@admin_required
def delete_user(user, user_id):
print(f"删除用户 {user_id}")
- 缓存优化:
def memoize(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def fibonacci(n):
return n if n < 2 else fibonacci(n-1) + fibonacci(n-2)
八、总结
装饰器的核心在于通过函数或类的组合实现代码复用,常见类型包括基础装饰器、参数化装饰器、类装饰器等。实际应用中需注意元信息保留、执行顺序等问题。通过灵活组合,可大幅提升代码的可维护性和扩展性。
三、优势和劣势
Python装饰器是Python语言中极具特色的功能,它通过高阶函数和闭包机制实现了对函数或类的动态增强。以下是装饰器在实际开发中的核心优势和劣势分析,结合多个技术文档进行深度解读:
一、装饰器的核心优势
1. 代码复用与模块化
装饰器通过将横切关注点(如日志记录、权限校验)从业务逻辑中抽离,显著提升代码复用率。例如:
from functools import wraps
def log_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[LOG] 调用 {func.__name__},参数:{args}")
result = func(*args, **kwargs)
return result
return wrapper
@log_decorator
def process_data(data):
# 业务逻辑
pass
- 优势:所有需要日志记录的函数只需添加
@log_decorator
,避免重复编写日志代码。 - 应用场景:日志记录、权限校验、缓存管理等。
2. 非侵入式代码增强
装饰器在不修改原函数代码的情况下动态添加功能,保持核心逻辑的纯净性。例如:
def retry(max_attempts=3):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
return wrapper
return decorator
@retry(max_attempts=5)
def api_call():
# 网络请求逻辑
pass
- 优势:无需修改
api_call
内部逻辑即可实现重试机制。
3. 提升代码可读性
通过装饰器将辅助功能与业务逻辑分离,使代码结构更清晰:
@cache
@validate_input
def compute(x, y):
# 核心计算逻辑
pass
- 优势:装饰器链明确展示功能层次(先校验输入,再缓存结果)。
4. 性能优化能力
内置装饰器(如@lru_cache
)可直接提升性能:
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
- 优势:通过缓存避免重复计算,时间复杂度从O(2^n)降至O(n)。
5. 灵活的参数化设计
装饰器支持参数传递,实现动态功能定制:
def throttle(delay):
def decorator(func):
last_call = 0
@wraps(func)
def wrapper(*args, **kwargs):
nonlocal last_call
current = time.time()
if current - last_call < delay:
raise Exception("调用频率过高")
last_call = current
return func(*args, **kwargs)
return wrapper
return decorator
@throttle(delay=1)
def high_frequency_api():
pass
- 优势:通过参数
delay
灵活控制函数调用频率。
二、装饰器的核心劣势
1. 调试复杂性
装饰器会修改函数的调用栈,导致调试时难以追踪原始函数:
@decorator_a
@decorator_b
def target():
pass
# 实际调用顺序:decorator_a → decorator_b → target
- 问题:调试器显示的调用链可能为
wrapper_a → wrapper_b → target
,掩盖原始函数名。
2. 性能开销
多层装饰器嵌套可能引入额外函数调用开销:
@log
@cache
@validate
def heavy_computation():
pass
- 问题:每个装饰器的包装函数都会增加调用层级,对高频调用函数影响显著。
3. 元信息丢失风险
未使用@wraps
的装饰器会覆盖原函数的__name__
、__doc__
等元数据:
def bad_decorator(func):
def wrapper():
return func()
return wrapper
@bad_decorator
def original():
"""原始文档"""
pass
print(original.__name__) # 输出:wrapper(而非original)
- 解决方案:强制使用
from functools import wraps
保留元信息。
4. 动态装饰限制
装饰器无法直接应用于已实例化的对象:
obj = ExistingClass()
# 无法直接对obj.method进行装饰
- 限制:需通过类继承或猴子补丁(monkey-patching)间接实现。
5. 过度设计风险
滥用装饰器可能导致代码结构复杂化:
@decorator1
@decorator2
@decorator3
@decorator4
def over_decorated():
pass
- 问题:装饰器链过长会降低代码可读性和维护性。
三、最佳实践建议
-
单一职责原则
每个装饰器只负责一个功能(如日志、缓存),避免功能混杂。 -
性能敏感场景慎用
高频调用函数尽量减少装饰器层级,必要时通过__slots__
或C扩展优化。 -
强制保留元信息
所有装饰器内部函数必须使用@wraps(func)
。 -
文档与类型提示
为装饰器添加详细文档和参数类型注解:def retry(max_attempts: int = 3) -> Callable: """重试装饰器""" def decorator(func: Callable) -> Callable: @wraps(func) def wrapper(*args, **kwargs) -> Any: # 实现逻辑 pass return wrapper return decorator
四、总结
Python装饰器是一把双刃剑:
✅ 优势:通过非侵入式增强、模块化设计和灵活的参数化,显著提升代码质量和开发效率。
❌ 劣势:调试困难、性能开销和过度设计风险需谨慎对待。
合理运用装饰器需遵循"适度原则",在代码简洁性、可维护性和性能之间找到平衡点。
四、总结
文章主要围绕Python装饰器展开,详细讲解了其多种类型及实现方式,通过丰富的代码示例让读者清晰理解装饰器的使用。同时,深入探讨了装饰器在实际开发中的优势,如代码复用与模块化、非侵入式代码增强、提升代码可读性、性能优化能力、灵活的参数化设计等,以及劣势,包括调试复杂性、性能开销、元信息丢失风险、动态装饰限制、过度设计风险等,并给出了相应的最佳实践建议,以指导读者合理运用装饰器,平衡代码简洁性、可维护性和性能。