大家好,我是涛哥,本文内容来自 涛哥聊Python ,转载请标原创。
更多Python学习内容:http://ipengtao.com
在Python编程中,装饰器(decorators)是一种非常强大的工具,可以在不修改原有函数代码的前提下,扩展函数的功能。而在编写装饰器时,functools
模块中的@wraps
装饰器是一个不可或缺的工具。本文将详细介绍@wraps
的作用和使用方法,并通过示例代码展示其实际应用。
一、什么是装饰器?
在深入了解@wraps
之前,我们先简单介绍一下装饰器。装饰器本质上是一个高阶函数,它接受一个函数作为参数,并返回一个新函数。装饰器常用于在函数执行前后添加额外的行为,如日志记录、访问控制、缓存等。
示例代码:
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
在这个示例中,my_decorator
是一个装饰器,它在目标函数say_hello
执行前后打印额外的信息。
二、@wraps的作用
当我们使用装饰器时,装饰后的函数实际上是包装器函数(wrapper function),而不是原来的函数。这会导致一些问题,例如:
- 原函数的名称和文档字符串(docstring)被包装器函数覆盖。
- 原函数的属性(如
__name__
和__doc__
)丢失。
为了解决这些问题,Python提供了functools.wraps
装饰器,它可以帮助我们保留原函数的元数据。
示例代码:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
@my_decorator
def say_hello():
"""This is the docstring of say_hello function."""
print("Hello!")
print(say_hello.__name__) # 输出: say_hello
print(say_hello.__doc__) # 输出: This is the docstring of say_hello function.
在这个示例中,使用@wraps
装饰wrapper
函数后,say_hello
函数的__name__
和__doc__
属性得到了保留。
三、@wraps的使用方法
@wraps
是functools
模块中的一个装饰器,它接受一个函数作为参数,并返回一个装饰器。@wraps
通常用于装饰包装器函数,以便将原函数的元数据复制到包装器函数中。
基本用法:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 执行装饰器逻辑
return func(*args, **kwargs)
return wrapper
使用示例:
以下是一个更复杂的示例,展示如何使用@wraps
在记录函数执行时间的装饰器中保留原函数的元数据。
import time
from functools import wraps
def timing_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function {func.__name__} took {end_time - start_time:.4f} seconds to execute")
return result
return wrapper
@timing_decorator
def slow_function():
"""A function that simulates a slow process."""
time.sleep(2)
print("Slow function executed")
slow_function()
print(slow_function.__name__) # 输出: slow_function
print(slow_function.__doc__) # 输出: A function that simulates a slow process.
在这个示例中,timing_decorator
装饰器用于计算并打印目标函数的执行时间。通过使用@wraps
装饰wrapper
函数,我们保留了原函数slow_function
的__name__
和__doc__
属性。
四、@wraps的详细说明
@wraps
实际上是functools.update_wrapper
函数的一个简化版。update_wrapper
函数用于将目标函数的指定属性复制到包装器函数中。默认情况下,这些属性包括__module__
、__name__
、__qualname__
、__annotations__
和__doc__
。
示例代码:
from functools import update_wrapper
def my_decorator(func):
def wrapper(*args, **kwargs):
# 执行装饰器逻辑
return func(*args, **kwargs)
update_wrapper(wrapper, func)
return wrapper
使用update_wrapper
函数可以实现与@wraps
相同的效果。@wraps
只是一个方便的简化版,它接受与update_wrapper
相同的参数,并将这些参数传递给update_wrapper
。
参数说明:
wrapped
:被包装的函数。assigned
:默认值为WRAPPER_ASSIGNMENTS
,指定要复制的属性。updated
:默认值为WRAPPER_UPDATES
,指定要更新的属性。
from functools import WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES
def custom_wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):
def decorator(wrapper):
update_wrapper(wrapper, wrapped, assigned, updated)
return wrapper
return decorator
五、实际应用场景
1. 缓存结果
使用装饰器缓存函数的结果,避免重复计算:
from functools import wraps
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10)) # 输出: 55
print(fibonacci.__name__) # 输出: fibonacci
print(fibonacci.__doc__) # 输出: None
2. 权限检查
在Web应用中,使用装饰器进行权限检查:
from functools import wraps
def require_permission(permission):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not has_permission(permission):
raise PermissionError("Permission denied")
return func(*args, **kwargs)
return wrapper
return decorator
def has_permission(permission):
# 假设有一个权限检查的逻辑
return permission == "admin"
@require_permission("admin")
def admin_area():
"""This is the admin area."""
print("Welcome to the admin area")
try:
admin_area()
except PermissionError as e:
print(e)
print(admin_area.__name__) # 输出: admin_area
print(admin_area.__doc__) # 输出: This is the admin area.
总结
在Python中,@wraps
是一个非常有用的工具,特别是在编写装饰器时。它可以帮助我们保留原函数的元数据,确保代码的可读性和可维护性。通过本文的介绍和示例代码,应该已经掌握了@wraps
的基本用法和实际应用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。