python笔记35-装饰器

前言

python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。 很多python初学者学到面向对象类和方法是一道大坎,那么python中的装饰器是你进入Python高级语法大门的一道坎。

计算函数运行时间

假设你写了几个函数,有一天领导心血来潮说,你把每个函数的运行时长(结束时间-开始时间)统计下,作为一个python实习生的你可能会这样写

原始函数

import timedef func_a():
print("hello")
time.sleep(0.5)def func_b():
print("world")
time.sleep(0.8)if __name__ == '__main__':
func_a()
func_b()

添加运行时长

作为一个实习生的你,可能想到的解决办法如下

import timedef func_a():
start = time.time()
print("hello")
time.sleep(0.5)
end = time.time()
print("运行时长:%.4f 秒" % (end-start))def func_b():
start = time.time()
print("world")
time.sleep(0.8)
end = time.time()
print("运行时长:%.4f 秒" % (end-start))if __name__ == '__main__':
func_a()
func_b()

运行结果:

hello
运行时长:0.5009 秒
world
运行时长:0.8008 秒

上面的代码虽然满足了领导的要求,但是如果你写的函数很多的话,每个函数都这样去添加,会显得代码很臃肿,有很多重复代码。 有一天你边上的一个python老司机看了下你的代码,给你指了条路:装饰器

函数装饰器

装饰器可以写成函数式装饰器,也可以写成一个类装饰器,先从简单的函数装饰器开始学习。 python装饰器本质上就是一个函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。

runtime函数就是一个装饰器了,它对原函数做了包装并返回了另外一个函数,额外添加了一些功能。在函数上方使用@语法糖就可以调用这个装饰器了

import timedef runtime(func):
def wrapper():
start = time.time()
f = func()     # 原函数
end = time.time()
print("运行时长:%.4f 秒" % (end-start))
return f
return wrapper@runtime
def func_a():
print("hello")
time.sleep(0.5)@runtime
def func_b():
print("world")
time.sleep(0.8)if __name__ == '__main__':
func_a()
func_b()

运行结果

hello
运行时长:0.5001 秒
world
运行时长:0.8001 秒

函数带参数装饰器

上面的runtime就是一个简单的装饰器模型了,但并不强壮,如果函数里面带有参数,那就不管用了,并且函数的参数是不固定的,这时候就需要用到*args,**kwargs两兄弟了

import timedef runtime(func):
def wrapper(*args, **kwargs):
start = time.time()
f = func(*args, **kwargs)     # 原函数
end = time.time()
print("运行时长:%.4f 秒" % (end-start))
return f
return wrapper@runtime
def func_a(a):
print("hello"+a)
time.sleep(0.5)@runtime
def func_b(b, c="xx"):
print("world"+b+c)
time.sleep(0.8)if __name__ == '__main__':
func_a("a")
func_b("b", c="xxx")

类装饰器

关于__call__方法,不得不先提到一个概念,就是可调用对象(callable),我们平时自定义的函数、内置函数和类都属于可调用对象, 但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数 callable。 如果在类中实现了__call__方法,那么实例对象也将成为一个可调用对象

import timeclass runtime(object):
def __init__(self, func):
self.func = funcdef __call__(self, *args, **kwargs):
start = time.time()
f = self.func(*args, **kwargs)     # 原函数
end = time.time()
print("运行时长:%.4f 秒" % (end-start))
return f@runtime
def func_a(a):
print("hello"+a)
time.sleep(0.5)@runtime
def func_b(b, c="xx"):
print("world"+b+c)
time.sleep(0.8)if __name__ == '__main__':
func_a("a")
func_b("b", c="xxx")

装饰器带参数

快到年底了,领导说运行的速度先不要太快了,让客户先加钱,然后再以正常的速度显示,那么现在的需求是让每个函数的运行时间加50%,该如何实现呢? 这就到了装饰器的高级语法,装饰器也需要带上参数了

函数装饰器

import timedef runtime(slowly=1):
def wrapper(func):
def inner_wrapper(*args, **kwargs):
start = time.time()
f = func(*args, **kwargs)     # 原函数
end = time.time()
t = end-start
time.sleep((slowly-1)*t)  # 延迟效果
new_end = time.time()
print("运行时长:%.4f 秒" % (new_end-start))
return f
return inner_wrapper
return wrapper@runtime(1.5)
def func_a(a):
print("hello"+a)
time.sleep(0.5)@runtime(1.5)
def func_b(b, c="xx"):
print("world"+b+c)
time.sleep(0.8)if __name__ == '__main__':
func_a("a")
func_b("b", c="xxx")

类装饰器

import timeclass runtime(object):
def __init__(self, slowly=1):
self.slowly = slowlydef __call__(self, func):
def wrapper(*args, **kwargs):
start = time.time()
f = func(*args, **kwargs)     # 原函数
end = time.time()
t = end-start
time.sleep((self.slowly-1)*t)  # 延迟效果
new_end = time.time()
print("运行时长:%.4f 秒" % (new_end-start))
return f
return wrapper@runtime(1.5)
def func_a(a):
print("hello"+a)
time.sleep(0.5)@runtime(1.5)
def func_b(b, c="xx"):
print("world"+b+c)
time.sleep(0.8)if __name__ == '__main__':
func_a("a")
func_b("b", c="xxx")

使用场景

用哪些地方需要使用装饰器呢?

  • 如果你用过locust,设置权重会用到@task(1),
  • 如果你用过pytest框架,使用fixture功能的时候经常会用到@pytest.fixture(scope="function")
  • allure里面可以添加测试步骤 @allure.step('修改购物车')
  • 被大量使用于Flask和Django web框架中,检查是否被授权去使用一个web应用的端点(endpoint)。如 @login_required
  • 也可以自己写个装饰器添加日志

本文分享自微信公众号 - 从零开始学自动化测试(yoyoketang)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员的知识天地

五大人工智能流行编程语言对比,只要学会一种绝对不亏!

就像大多数软件应用程序的开发一样,开发人员也在使用多种语言来编写人工智能项目,但是现在还没有任何一种完美的编程语言是可以完全速配人工智能项目的。

16000
来自专栏程序员的知识天地

女神把微信消息撤回后好慌,Python几十行代码轻松查看撤回消息!

曾几何时Python对我说:"时日已不多,速度学Python"。于是乎上天让我看到了一个基于python的微信开源库:itchat,玩的不亦乐乎,接着我做了一个...

11600
来自专栏程序员的知识天地

6大技巧,让Python编程健步如飞!

有人跟我抱怨说python太慢了,然后我就将python健步如飞的六大技巧传授给他,结果让他惊呆了,你也想知道这个秘诀吗?这就告诉你:

8300
来自专栏有趣的Python和你

Python数据分析师养成记

从这周开始,罗罗攀开始更新新系列《Python数据分析师养成记》。该系列将以小白的视角出发,一步步的进阶Python数据分析。

15200
来自专栏程序员的知识天地

10年程序员论:学习Python最正确的步骤(0基础必备)

首先,学习Python编程技术,自学或者参加培训学习都适用,每个人都有自己的学习方式和方法。

9000
来自专栏程序员的知识天地

使用Python进行面部合成,合成结果请忽略!

return_landmarkInt是否检测并返回人脸关键点。合法值为:2 检测。返回 106 个人脸关键点。1检测。返回 83 个人脸关键点。0不检测注:本参...

21200
来自专栏Python编程大咖

谷歌算法工程师力荐,这本书用大量案例教你学会Python编程,必看

因为智能技术,因为大数据,因为它的简便,所以越来越多的人学习Python,然而,也在学习中四处碰壁。

9600
来自专栏iOSDevLog

Latex

LaTeX 是一种标记语言(或者,如 官方网站 所述,“用于高质量排版的文档准备系统”) 用于创建精彩的论文和演示文稿。你在职业生涯中阅读的几乎所有论文都是使用...

29400
来自专栏JetpropelledSnake

Python关键点笔记之使用 pyenv 管理多个 Python 版本依赖环境

从接触Python以来,一直都是采用virtualenv和virtualenvwrapper来管理不同项目的依赖环境,通过workon、mkvirtualenv...

12700
来自专栏程序员的知识天地

为什么那么多自学Python的后来都放弃了,总结起来就这些原因

目前信息化产业发展势头很好,IT就成为了很多普通人想要涉及的行业,因为相比于传统行业,IT行业涨薪幅度大,机会也多,所以就会大批的人想要转行来学习Python开...

9700

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励