在Python面试中被问到什么是装饰器时该如何巧妙的去回答面试官?
前言
本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。
PS:如有需要Python学习资料的小伙伴可以点击下方链接自行获取
python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
很多python初学者学到面向对象类和方法是一道大坎,那么python中的装饰器是你进入Python高级语法大门的一道坎。
计算函数运行时间
假设你写了几个函数,有一天领导心血来潮说,你把每个函数的运行时长(结束时间-开始时间)统计下,作为一个python实习生的你可能会这样写
原始函数
添加运行时长
作为一个实习生的你,可能想到的解决办法如下
运行结果:
上面的代码虽然满足了领导的要求,但是如果你写的函数很多的话,每个函数都这样去添加,会显得代码很臃肿,有很多重复代码。
有一天你边上的一个python老司机看了下你的代码,给你指了条明路:装饰器
函数装饰器
装饰器可以写成函数式装饰器,也可以写成一个类装饰器,先从简单的函数装饰器开始学习。
python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
runtime函数就是一个装饰器了,它对原函数做了包装并返回了另外一个函数,额外添加了一些功能。在函数上方使用@语法糖就可以调用这个装饰器了
运行结果
函数带参数装饰器
上面的runtime就是一个简单的装饰器模型了,但并不强壮,如果函数里面带有参数,那就不管用了,并且函数的参数是不固定的,这时候就需要用到args,*kwargs两兄弟了
类装饰器
关于call方法,不得不先提到一个概念,就是可调用对象(callable),我们平时自定义的函数、内置函数和类都属于可调用对象,
但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数 callable。
如果在类中实现了call方法,那么实例对象也将成为一个可调用对象
装饰器带参数
快到年底了,领导说运行的速度先不要太快了,让客户先加钱,然后再以正常的速度显示,那么现在的需求是让每个函数的运行时间加50%,该如何实现呢?
这就到了装饰器的高级语法,装饰器也需要带上参数了
函数装饰器
类装饰器
使用场景
用哪些地方需要使用装饰器呢?
- 如果你用过locust,设置权重会用到@task(1),
- 如果你用过pytest框架,使用fixture功能的时候经常会用到@pytest.fixture(scope="function")
- allure里面可以添加测试步骤 @allure.step('修改购物车')
- 被大量使用于Flask和Django web框架中,检查是否被授权去使用一个web应用的端点(endpoint)。如 @login_required
- 也可以自己写个装饰器添加日志
前面一篇对python装饰器有了初步的了解了,但是还不够完美,领导看了后又提出了新的需求,希望运行的日志能显示出具体运行的哪个函数。
name和doc
__ name __用于获取函数的名称, __ doc __用于获取函数的docstring内容(函数的注释)
装饰器加函数名称日志
在装饰器里面添加2行代码,打印正在运行函数的名称和docstring内容
运行结果
从运行的结果可以看出,func_a.__ name __ 运行的结果是wrapper,func_a. __ doc__运行的结果是wrapper inner fuction。
也就是说被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),那这个问题如何解决呢?
这就需要用到functools里面的一个wraps函数了
functools
当func_a函数被装饰后,导致了一个副作用:自身的函数属性和docstring内容变成了wrapper函数的属性了。
这里需用到functools里面的一个wraps的装饰器来消除这样的副作用。
只需在wrapper函数上加上@wraps(func)即可解决
运行结果
类装饰器
带参数的装饰器,可以写成类装饰器
运行结果