当你在处理大批量的数据时,你想测试一下你写的代码要运行多久,如果你用的是anaconda,那没问题,可以直接%%time来计算此单元格的运行时间,呢么在Pycharm上要怎么做呢,你可以导入time模块去计算一个时间差,但是如果多段代码都需要分别计算执行时间,需要在每个函数上都写一边计时代码吗? 这样岂不是很麻烦,这时我们可以利用装饰器来实现,不仅可以减少重复代码,而且可以在不改变原来代码的基础上增加新的功能。
在说装饰器之前,先说一下闭包函数。Python是一种面向对象的编程语言,在Python中一切皆为对象,所以变量所拥有的属性,函数同样可以拥有,所以,在函数内部再创建一个函数是完全合理的。这种函数称为内嵌函数。这种函数只可以在外部函数的作用域内被正常调用,在外部函数的作用域之外调用会报错,例如:
而如果内部函数里引用了外部函数里定义的对象,那么此时内部函数就被称为闭包函数。
举一个闭包的例子:
def test():
a = 1
def sum():
b = 2
return a + b
return sum
内部函数调用了自由变量a,外部函数返回内部函数,形成闭包。
Python中的装饰器本身也是一个函数,它可以实现在不改变原函数的基础上增加新的功能,装饰器的返回值也是一个函数对象。装饰器函数的外部函数传入我要装饰的函数名字,返回经过修饰后函数的名字;内层函数(闭包)负责修饰被修饰函数。
例如为原函数增加新的功能:
def decorator(func):
def inner():
print('new function1')
func()
print('new function2')
return inner
@decorator
def test():
print('old function')
if __name__ == '__main__':
test()
运行结果如下:
@decorator这个语法相当于 执行 func = decorator(func),为func函数装饰并返回。
如果原函数需要传参怎么办?此时我们需要修改装饰器代码,用到Python中的*args与**kwargs*args表示的参数以列表的形式传入,**kwargs表示的参数以字典的形式传入举个例子:
def test(*args, **kwargs):
print(f'args:{args}')
print(f'kwargs:{kwargs}')
test(1, 2, 3, [1, 2, 3], a=1, b=2)
结果如下:(不懂的可以百度- . -)
原函数带参数,装饰器该怎么写?
def decorator(func):
def inner(*args, **kwargs):
print('new function1')
res = func(*args, **kwargs)
print('new function2')
return res
return inner
@decorator
def test(x):
print('old function')
return f'传入的参数x为:{x}'
if __name__ == '__main__':
res = test(3)
print(res)
原函数返回了某个值,因此在内增函数执行func时,同样需要将这个值返回。可以调整需要装饰的函数在内层函数的位置,即可以实现新增工能在需要装饰的函数之前执行还是之后执行。运行效果如下:
你以为到这一步就算完成了吗? 当然不是~ 所以请看下面代码:(只是修改了main下面的代码)
def decorator(func):
def inner(*args, **kwargs):
print('new function1')
res = func(*args, **kwargs)
print('new function2')
return res
return inner
@decorator
def test(x):
print('old function')
return f'传入的参数x为:{x}'
if __name__ == '__main__':
print(test.__name__)
运行效果如下:
为什么函数的__name__变成了inner而不是test呢,因为被装饰函数的信息在此过程中丢失了,需要借助functools来保持原有信息。
from functools import wraps
def decorator(func):
@wraps(func)
def inner(*args, **kwargs):
print('new function1')
res = func(*args, **kwargs)
print('new function2')
return res
return inner
@decorator
def test(x):
print('old function')
return f'传入的参数x为:{x}'
if __name__ == '__main__':
print(test.__name__)
运行结果如下: