接下来。装逼开始....
实现一个简单装饰器
示例1:计算并输入函数运行时间
def decorator_time(func):
def wrapper_time():
time_data = time.perf_counter()
results = func()
count_time = time.perf_counter() - time_data
print(f'函数运行时间:[{count_time :.7f}]->函数名称:[{func.__name__}]->运行结果:[{results}]')
return results
return wrapper_time
@decorator_time
def calculation():
return tuple(i for i in [1, 2, 3, 4])
if __name__ == '__main__':
print(calculation())
函数运行时间:[0.0000041]->函数名称:[calculation]->运行结果:[(1, 2, 3, 4)]
(1, 2, 3, 4)
Process finished with exit code 0
func就是前面文章所说的自由变量,results=func()就是引用自由变量
return wrapper_time 就是返回的内部函数,并取代被装饰的函数
工作原理
@decorator_time
def calculation():
return tuple(i for i in [1, 2, 3, 4])
其实就等价于:
@decorator_time
def calculation():
return tuple(i for i in [1, 2, 3, 4])
calculation = decorator(calculation)
为什么会这样呢?
因为calculation会作为函数参数传给decorator_time,然后decorator_time会返回wrapper_time函数,python会把wrapper_time赋值给calculation。
示例2:当前.py文件中获取被装饰函数的名称
if __name__ == '__main__':
print(calculation.__name__)
wrapper_time
Process finished with exit code 0
所以。现在的calculation保存的是wrapper_time函数的引用,每次调用calculation(),执行的都是wrapper_time()
装饰器就是把装饰的函数替换成新的函数,二者接受相同的参数,返回被装饰的函数本该返回的值,同时还会做一些额外操作(对裸露的身体增加衣服进行修饰打扮)
如果不需要传参的函数,可以使用上面的装饰,遇到必须要传参的函数,就得在装饰器上增加接收参数的位置...
示例3:入参的装饰器,此装饰器是给原函数增加了处理参数的功能
import time
from functools import wraps
def decorator_values(func):
@wraps(func)
def wrapper_values(*args, **kwargs):
"""
我是装饰器函数
:param args:
:param kwargs:
:return:
"""
time_data = time.perf_counter()
func(*args, **kwargs)
data_list = []
if args:
data_list.append(args)
if kwargs:
data_list.append(kwargs)
count_time = time.perf_counter() - time_data
print(f'函数运行时间:{count_time :.7f}')
return data_list
return wrapper_values
@decorator_values
def get_values(a):
"""
我是被装饰函数
:param a:
:return:
"""
return a
if __name__ == '__main__':
print(get_values(10, b=15))
函数运行时间:0.0000028
[(10,), {'b': 15}]
*args和**kwargs是接收元组(也可以接受单参数)和字典(也可以处理关键字参数)的关键字
为什么要增加@wraps内置装饰器?
示例4:增加内置装饰器@wraps
print(get_values.__name__)
print(get_values.__doc__)
print(get_values.__code__)
get_values
我是被装饰函数
:param a:
:return:
<code object wrapper_values at 0x7fcef8ed9d40, file "/Users/lifeng/python-projects/Test/pythonScripts/python_decorator.py", line 25>
Process finished with exit code 0
示例5:不增加内置装饰器@wraps
print(get_values.__name__)
print(get_values.__doc__)
print(get_values.__code__)
wrapper_values
我是装饰器函数
:param args:
:param kwargs:
:return:
<code object wrapper_values at 0x7f8a43dd9d40, file "/Users/lifeng/python-projects/Test/pythonScripts/python_decorator.py", line 25>
Process finished with exit code 0
从上述示例中可以看到,增加了内置装饰器@wraps,其实就是把相关属性从func上复制到wrapper_values中...
综上所述的都是装饰器函数,既然有函数装饰器,那肯定也会有类装饰器
示例6:不带参数的类装饰器
class DecoratorClass:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)
魔术方法
-
__init__ :初始化(实例创建),也是俗称的构造方法,应该都懂,不多叙述...
-
__call__ :可调用对象
示例7:使用魔术方法__call__和不使用的区别
class A:
def __init__(self):
self.name = '小狗'
def get_name(self):
print(self.name)
a = A()
a.get_name()
class B:
def __init__(self):
self.name = '小狗'
def __call__(self):
print(self.name)
b = B()
b()
小狗
小狗
Process finished with exit code 0
魔术方法__call__就是简化了对象中方法的调用 (当某方法调用频率很高)...
示例8:带参数的类装饰器
class DecoratorValues:
def __init__(self, code=None):
self.code = code
def __call__(self, func):
@wraps(func)
def class_wraps(*args, **kwargs):
time_data = time.perf_counter()
func(*args, **kwargs)
data_list = []
if args:
data_list.append(args)
if kwargs:
data_list.append(kwargs)
count_time = time.perf_counter() - time_data
print(f'函数运行时间:{count_time :.7f}')
return data_list
return class_wraps
@DecoratorValues()
def get_values(g):
return g
if __name__ == '__main__':
print(get_values(10))
注意:带参数的装饰类,方块糖引用使用时千万不要忘了小括号( @DecoratorValues() ),小括号时调用,不加括号返回的是一个可调用的call对象...
至此,对装饰器有了初步了解和使用,后面会继续探讨内置装饰器及如何装饰器参数化等...
以上总结或许能帮助到你,或许帮助不到你,但还是希望能帮助到你,如有疑问、歧义,评论区留言会及时修正发布,谢谢!
未完,待续…
一直都在努力,希望您也是
微信搜索公众号:就用python