Python装饰器是一种用于修改函数或类的行为的特殊语法构造。装饰器允许在不修改原始代码的情况下,通过将函数或类包装在另一个函数中来添加额外的功能。
装饰器通常用于以下场景:
下面是一个简单的示例,展示如何定义和应用一个装饰器:
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
# 在调用原始函数之前执行的代码
print("Before the function is called")
# 调用原始函数
result = original_function(*args, **kwargs)
# 在调用原始函数之后执行的代码
print("After the function is called")
# 返回结果
return result
# 返回包装函数
return wrapper_function
# 应用装饰器
@decorator_function
def my_function():
print("Inside the function")
# 调用被装饰的函数
my_function()
输出结果:
sqlCopy code
Before the function is called
Inside the function
After the function is called
在上面的例子中,decorator_function
是一个装饰器函数,它接受一个原始函数作为参数,并返回一个包装函数 wrapper_function
。包装函数在调用原始函数之前和之后执行额外的代码,从而实现了函数行为的修改。
装饰器使用 @
符号应用到目标函数上。在上述例子中,通过 @decorator_function
将装饰器应用到 my_function
上,使得 my_function
在调用前后会执行装饰器中定义的代码。
在装饰器中,def wrapper_function(*args, **kwargs)
定义了一个包装函数,它接受任意数量的位置参数 args
和关键字参数 kwargs
。这种形式的参数定义使用了 *
和 **
运算符,允许将不定数量的参数传递给函数。
*args
表示将多个位置参数打包成一个元组(tuple),在函数内部可以通过 args
来访问这个元组。这样,当原始函数被调用时,可以将传递给原始函数的所有位置参数捕获到 args
中,然后在包装函数内部调用原始函数时,再将这些参数传递给原始函数。
类似地,**kwargs
表示将多个关键字参数打包成一个字典(dictionary),在函数内部可以通过 kwargs
来访问这个字典。这样,当原始函数被调用时,可以将传递给原始函数的所有关键字参数捕获到 kwargs
中,然后在包装函数内部调用原始函数时,再将这些参数传递给原始函数。
使用 *args
和 **kwargs
的好处是,装饰器可以适应不同原始函数的参数签名。无论原始函数接受多少个参数或关键字参数,装饰器都能正确地捕获和传递这些参数,使得装饰器对不同函数的适用性更广泛。
在包装函数内部,可以根据需要访问和操作 args
和 kwargs
,例如可以对它们进行修改、添加新的参数等。然后,在调用原始函数时,可以使用 *args
和 **kwargs
语法来将捕获的参数传递给原始函数,确保参数的正确传递。
需要注意的是,在定义装饰器时,*args
和 **kwargs
只是约定俗成的名称,并不是固定的。你可以选择其他的名称,但建议使用习惯用法,这样可以增加代码的可读性,让其他开发者更容易理解你的代码意图。
总之,def wrapper_function(*args, **kwargs)
定义了一个接受任意数量位置参数和关键字参数的包装函数,使得装饰器可以适用于不同的函数签名。
Python类装饰器是一种装饰器形式,其中装饰器本身是一个类,而不是函数。类装饰器通过实现__call__()
方法来使实例对象可调用,从而实现对函数或类的装饰。
下面是一个简单的示例,展示如何定义和应用一个类装饰器:
class DecoratorClass:
def __init__(self, original_function):
self.original_function = original_function
def __call__(self, *args, **kwargs):
# 在调用原始函数之前执行的代码
print("Before the function is called")
# 调用原始函数
result = self.original_function(*args, **kwargs)
# 在调用原始函数之后执行的代码
print("After the function is called")
# 返回结果
return result
# 应用装饰器
@DecoratorClass
def my_function():
print("Inside the function")
# 调用被装饰的函数
my_function()
输出结果:
sqlCopy code
Before the function is called
Inside the function
After the function is called
在上面的例子中,DecoratorClass
是一个类装饰器,它接受一个原始函数作为参数,并在 __init__()
方法中保存该函数的引用。通过实现 __call__()
方法,该类的实例可以被调用,即实现了可调用对象的行为。
在 __call__()
方法中,类装饰器定义了在调用原始函数之前和之后执行的代码。通过 self.original_function
引用原始函数,并将传递给装饰器的参数 *args
和 **kwargs
传递给原始函数。
装饰器通过 @DecoratorClass
语法应用到目标函数上。在上述例子中,通过 @DecoratorClass
将装饰器应用到 my_function
上,使得 my_function
在调用前后会执行装饰器中定义的代码。
Python装饰器支持嵌套,也就是说一个装饰器可以被另一个装饰器所装饰。通过嵌套装饰器,可以在函数或类上应用多个装饰器,从而按照特定的顺序为其添加不同的功能。
下面是一个示例,展示了如何使用嵌套装饰器:
def decorator1(original_function):
def wrapper_function(*args, **kwargs):
print("Decorator 1 - Before the function is called")
result = original_function(*args, **kwargs)
print("Decorator 1 - After the function is called")
return result
return wrapper_function
def decorator2(original_function):
def wrapper_function(*args, **kwargs):
print("Decorator 2 - Before the function is called")
result = original_function(*args, **kwargs)
print("Decorator 2 - After the function is called")
return result
return wrapper_function
@decorator1
@decorator2
def my_function():
print("Inside the function")
my_function()
输出结果:
Decorator 1 - Before the function is called
Decorator 2 - Before the function is called
Inside the function
Decorator 2 - After the function is called
Decorator 1 - After the function is called
在上述示例中,decorator1
和 decorator2
是两个装饰器函数。可以看到,它们的定义与之前的例子中的装饰器函数相同。
通过在 my_function
上使用 @decorator1
和 @decorator2
,这两个装饰器被嵌套应用到 my_function
上。当调用 my_function
时,装饰器的顺序是从下到上,即先应用 decorator2
,再应用 decorator1
。
装饰器的嵌套顺序非常重要,因为它决定了装饰器的执行顺序。在上述示例中,先执行 decorator2
的代码,然后再执行 decorator1
的代码。这种顺序可以根据实际需求进行调整。
需要注意的是,装饰器的嵌套可以有多层,你可以根据需要在函数或类上应用多个装饰器。每个装饰器都会对目标函数或类进行修改或扩展,从而实现不同的功能。
通过装饰器的嵌套,你可以灵活地组合和应用多个装饰器,以满足特定的需求,并在不修改原始代码的情况下为函数或类添加各种功能。
你登录微信,需要输入用户名密码,然后点击确认,这样,服务器端便会查询你的用户名是否存在、是否和密码匹
配等等。如果认证通过,你就可以顺利登录;如果不通过,就抛出异常并提示你登录失败。
再比如一些网站,你不登录也可以浏览内容,但如果你想要发布文章或留言,在点击发布时,
服务器端便会查询你是否登录。如果没有登录,就不允许这项操作等等
import functools
def authenticate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
request = args[0]
if check_user_logged_in(request): # 如果用户处于登录状态
return func(*args, **kwargs) # 执行函数post_comment()
else:
raise Exception('Authentication failed')
return wrapper
@authenticate
def post_comment(request, ...)
这段代码中,我们定义了装饰器 authenticate;而函数 post_comment(),则表示发表用户 对某篇文章的评论。每次调用这个函数前,都会先检查用户是否处于登录状态,如果是登录状 态,则允许这项操作;如果没有登录,则不允许。
日志记录
日志记录同样是很常见的一个案例。在实际工作中,如果你怀疑某些函数的耗时过长,导致整 个系统的 latency(延迟)增加,所以想在线上测试某些函数的执行时间,那么,装饰器就是 一种很常用的手段。
日志记录同样是很常见的一个案例。在实际工作中,如果你怀疑某些函数的耗时过长,导致整 个系统的 latency(延迟)增加,所以想在线上测试某些函数的执行时间,那么,装饰器就是 一种很常用的手段。
import time
import functools
def log_execution_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
res = func(*args, **kwargs)
end = time.perf_counter()
print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
return res
return wrapper
@log_execution_time
def calculate_similarity(items):
这里,装饰器 log_execution_time 记录某个函数的运行时间,并返回其执行结果。如果你想 计算任何函数的执行时间,在这个函数上方加上@log_execution_time即可。
在大型公司的机器学习框架中,我们调用机器集群进行模型训练前,往往会用装饰器对其输入 (往往是很长的 JSON 文件)进行合理性检查。这样就可以大大避免,输入不正确对机器造 成的巨大开销。
import functools 2
3 def validation_check(input):
4 @functools.wraps(func)
5 def wrapper(*args, **kwargs):
6 ... # 检查输入是否合法
7
8 @validation_check
9 def neural_network_training(param1, param2, ...):