目录
什么是装饰器
装饰器举例
@语法糖
类装饰器
***装饰器的高级用法***
带参数的装饰器
改成类装饰器
缓存装饰器
Flask中的路由装饰器
看看装饰器在官方文档上的解释:
一个函数返回另一个函数,通常以 @wrapper
语法作为函数转换的方式应用。装饰器的常见示例包括 classmethod()
和 staticmethod()
。
装饰器语法仅仅是一种语法糖,以下两种函数定义在语义上是等价的:
def f(arg):
...
f = staticmethod(f)
@staticmethod
def f(arg):
...
相同的概念也存在于类中,但在类中的使用较少。有关装饰器的更多信息,请参阅有关函数定义和类定义的文档。
首先定义一个判断是否为质数的函数
def is_prime(num): #判断是否为质数
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
现在我想增加一个时间逻辑,以便于性能调优,使用装饰器的思想用来记录is_prime(10000)需要的时间
import time
def is_prime(num): # 判断是否为质数
time.sleep(1)
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
def count_time(func):
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args, **kwargs)
print("执行时间为:", time.time() - t1)
return wrapper
is_prime = count_time(is_prime) #装饰器count_time返回函数对象wrapper,相当于is_prime=wrapper
is_prime(10000) #执行时间为: 1.0012288093566895
这里的count_time就是装饰器,装饰器内定义wrapper函数,把func这个函数当做参数传入,在wrapper函数中写入时间逻辑。
Debug后可以发现is_prime(10000)下一步会直接进入wrapper,也符合我注释里写的
Python 提供了 @
语法糖,使得在函数或类上应用装饰器更加简洁和可读。@在项目中非常常见,我们经常会发现自己调用的外部函数或类中有@
上面提到的装饰器可以用@cout_time替换
is_prime = count_time(is_prime)
import time
def count_time(func):
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args, **kwargs)
print("执行时间为:", time.time() - t1)
return wrapper
@count_time
def is_prime(num): # 判断是否为质数
time.sleep(1)
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
if __name__ == "__main__":
# is_prime = count_time(is_prime)
is_prime(10000)
上面提到的是函数装饰器,接下来聊聊类装饰器。
__call__ 方法,允许对象像函数一样被调用。
import time
class CountTime:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
t2 = time.time()
result = self.func(*args, **kwargs)
print("执行时间为:", time.time() - t2)
return result
def is_prime(num): # 判断是否为质数
time.sleep(1)
if num < 2:
return False
elif num == 2:
return True
else:
for i in range(2, num):
if num % i == 0:
return False
return True
if __name__ == "__main__":
# is_prime = CountTime(is_prime)
is_prime(10000)
流程和函数装饰器类似,通过__init__()方法初始化类,再通过__call__()方法调用装饰方法。
由于类装饰器可以更好地组织代码,它们通常可以提高代码的可读性和可维护性。这其实也是类的优点。
有时候,你可能想要创建一个装饰器,它接受一些参数来自定义装饰器的行为。这可以通过在装饰器内部再嵌套一个函数来实现。例如:
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
不使用语法糖@
# 手动应用装饰器到函数 say_hello
say_hello = repeat(3)(say_hello)
repeat(3)(say_hello),第一次看到这代码确实有点懵,下面来讲讲该程序的流程
1.调用 say_hello = repeat(3)(say_hello)
时,首先调用 repeat(3)
。这将返回 decorator
函数,但并不会立即执行它。此时,decorator
函数已经捕获了参数 n
。
2.接下来,再次调用 decorator(say_hello)
,这将返回 wrapper
函数,但同样不会立即执行它。此时,wrapper
函数已经捕获了参数 func
,这里的 func
是外部的 say_hello
函数。
3.调用 say_hello()
时,实际上是在执行 wrapper(*args, **kwargs)
,其中 *args
和 **kwargs
对应于 say_hello
函数的参数。在这里,wrapper
函数将 say_hello
函数重复执行了三次。
class RepeatDecorator:
def __init__(self, n):
self.n = n
def __call__(self, func):
def wrapper(*args, **kwargs):
for _ in range(self.n):
func(*args, **kwargs)
return wrapper
@RepeatDecorator(3)
def say_hello():
print("Hello!")
say_hello()
缓存装饰器是一种常见的装饰器类型,用于存储函数的计算结果,以便在后续相同输入的情况下可以直接返回缓存的结果,而不必重新计算。这可以显著提高函数的性能,尤其是在计算密集型或IO密集型的任务中。
下面是一个简单的缓存装饰器的示例:
def cache(func):
# 创建一个字典用于缓存结果
cache_dict = {}
def wrapper(*args, **kwargs):
# 构建缓存键,将参数元组和关键字参数转化为字符串
key = str(args) + str(kwargs)
# 如果结果已经在缓存中,则直接返回缓存的结果
if key in cache_dict:
return cache_dict[key]
# 否则,调用原始函数计算结果
result = func(*args, **kwargs)
# 将结果存入缓存字典
cache_dict[key] = result
return result
return wrapper
使用缓存装饰器的步骤如下:
定义一个带有缓存装饰器的函数。
装饰目标函数,例如:
@cache
def expensive_function(n):
# 模拟耗时的计算任务
result = sum(range(n))
return result
现在,expensive_function
函数将自动使用缓存。如果相同的参数传递给函数,它将首先检查缓存并返回已存储的结果,而不会重新计算。
缓存装饰器的优点包括:
提高性能:避免了相同输入的重复计算,节省了时间和计算资源。
简化代码:无需在每个需要缓存的函数内部手动实现缓存逻辑,装饰器为您处理。
可配置性:您可以根据需要灵活地调整缓存策略,例如设置缓存过期时间。
需要注意的是,缓存装饰器的实现可以更加复杂,可以根据具体需求添加过期时间、缓存大小限制等功能。另外,使用缓存时应该小心,确保不会导致内存占用过多或者缓存数据变得过时。
@app.route()
这是 Flask 中最常用的路由注册装饰器,它用于将 URL 路径与视图函数关联起来。你可以在装饰器中指定 URL 路径以及支持的 HTTP 请求方法。例如:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '欢迎访问首页'
@app.route('/about')
def about():
return '关于我们'
上述代码中,@app.route('/')
将根路径关联到 index
视图函数,@app.route('/about')
将 "/about" 路径关联到 about
视图函数。默认情况下,这些路由仅支持 GET 请求,但您可以使用 methods
参数来指定其他请求方法。
@app.route()
装饰器的可选参数
methods
:指定支持的 HTTP 请求方法,例如 methods=['GET', 'POST']
。defaults
:为视图函数的参数提供默认值,例如 defaults={'name': 'Guest'}
。host
:指定要匹配的主机名,例如 host='example.com'
。subdomain
:指定子域名,例如 subdomain='api'
。动态路由
你还可以在 URL 路径中使用变量,这样可以捕获 URL 中的特定值并将其传递给视图函数。例如:
@app.route('/user/')
def show_user(username):
return f'用户: {username}'
在这个例子中,
是一个动态变量,它会匹配 URL 中的任何值,并将其传递给 show_user
视图函数作为参数。
路由装饰器的返回值
路由注册装饰器通常不返回任何值,但是您可以在视图函数内部使用 return
语句来返回响应内容,这将作为 HTTP 响应发送给客户端。例如:
@app.route('/')
def index():
return '欢迎访问首页'
在这个示例中,'欢迎访问首页'
字符串将作为 HTML 响应返回给客户端。