一.装饰器概念
装饰器是一个高阶函数,可以动态修改函数或类的行为,例如日志记录,性能分析,权限控制和缓存。
装饰器概念:本质上是一个函数,它可以接收一个函数作为参数并返回一个新的函数。这个函数是对原函数的“包装”和“增强”,可以在不改变原函数代码的前提下,增加额外的功能。
装饰器工作过程:
- 定义装饰器:定义一个装饰器函数,该函数接收一个函数作为参数。
- 定义内部函数:在装饰器函数内部定义一个函数(wrapper),这个函数会调用原函数,并且在调用前后添加其他功能。
- 返回内部函数:装饰器函数返回内部函数。
- @语法:在需要被装饰的函数前使用@装饰器名称,Python解释器会自动将函数名作为参数传递给装饰器函数,并返回新的函数。
二.装饰器的应用
1.原函数带参数
import time
def decorator(func): # 装饰器函数
def wrapper(*args, **kwargs): #内部函数
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f'{func.__name__} execution time: {end_time-start_time} seconds')
return wrapper
@decorator
def square(x):# 原函数
res = x**2
print(res)
return res
square(10) #调用函数
"""
100
square execution time: 2.5987625122070312e-05 seconds
"""
装饰器函数decorator,输出函数运行时间,如果被装饰的函数带参数,装饰器内部的函数wrapper可以接收,通过*args和**kwargs实现。
2.带参数的装饰器
使用一个外层函数封装装饰器,可以向装饰器本身传递参数,依据不同的条件实现不同的装饰器功能。
import time
def timer(threshold): # 外层函数
def decorator(func): # 装饰器函数
def wrapper(*args, **kwargs): # 内部函数
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
if end_time - start_time > threshold: # 条件
print(f'{func.__name__} took longer than {threshold} seconds')
return result
return wrapper
return decorator
@timer(0.2) # 等价于 my_sleep = timer(0.2)(my_sleep)
def my_sleep():
time.sleep(0.4)
my_sleep() # square took longer than 0.2 seconds
3.functool.wraps
print(my_sleep.__name__) # 打印函数名称 wrapper
装饰后函数元数据会丢失(如函数名称,文档字符串等), 如果想要保留这些元数据,可以使用functool.wraps装饰器。
import functools
def decorator(func): # 装饰器函数
@functools.wraps(func)
def wrapper(*args, **kwargs): #内部函数
func(*args, **kwargs)
return wrapper
@decorator
def square(x):
print('This is a decorated function')
print(square.__name__) # square
装饰器的优点:提高了代码的可重用性,避免重复冗余代码,
4.类装饰器
装饰器不一定是函数,也可以是类,使用方法和函数类似。
类装饰器中,__call__魔术方法实现对原函数的调用和额外的逻辑代码编写。
class logging(object): # 装饰器类
def __init__(self, func): # 传递的是test方法
self.func = func
def __call__(self, *args, **kwargs): # 接受任意参数
print("{} is running.".format(self.func.__name__))
return self.func(*args, **kwargs)
@logging
def test(a, b):
print(a+b)
test(2, 7)
"""
test is running.
9
"""
类作为一个装饰器,首先使用__init__方法实例化函数,实例化func后,使用self.func即可调用函数,然后调用__call__装饰函数。
类装饰器也可以传递参数,使用__init__魔术方法 。
from functools import wraps
class logging(object):
def __init__(self, logfile="day.log"): # 传递装饰器参数
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
print(self.logfile)
return func(*args, **kwargs)
return wrapper
@logging(logfile='day1.log')
def test(num):
print(num)
test(10)
"""
day1.log
10
"""
5.装饰器顺序
一个函数可以同时定义多个装饰器,例如:
@d1
@d2
@d3
def func():
print('A sample')
多个装饰器的执行顺序是从里到外,先调用最里面的装饰器,再调用最外层的装饰器,等价于:
f = d1(d2(d3(func)))
419

被折叠的 条评论
为什么被折叠?



