Python装饰器有哪些绝妙的用法
作者:爱摸鱼的菜鸟码农. 发布时间:2022-09-08 01:09:47
装饰器的价值不言而喻,可以用来增强函数功能、简化代码、减少代码冗余。
它的使用场景同样很多,比较简单的场景包含打印日志、统计运行时间,这类例子和用法网上已经很多了:
def time_dec(func):
def wrapper(*arg):
t = time.clock()
res = func(*arg)
print func.func_name, time.clock()-t
return res
return wrapper
@time_dec
def myFunction(n):
...
再进阶一些的,可以用来校验函数传入参数类型、线程同步、单元测试等:
@parameters(
(2, 4, 6),
(5, 6, 11),
)
def test_add(a, b, expected):
assert a + b == expected
目前可以用的装饰器可以分为如下几类:
自定义
第三方工具包
内置
下面就分别来介绍一下。
自定义
关于自定义的装饰器在前面已经提到了,我在开发过程中经常用到的就是日志打印、计时、数据校验等场景,通过装饰器可以提高代码的简洁性,避免重复造轮子。
除了这些基本的,也有一些比较实用的地方。
作为开发同学,肯定会遇到不同的运行环境:
开发环境
测试环境
生产环境
有时候,我们期望一个函数在不同环境下执行不同的过程,产出不同的结果,做一些环境的隔离和差异化处理。
通过装饰器就可以很好的解决:
production_servers = [...]
def production(func: Callable):
def inner(*args, **kwargs):
if gethostname() in production_servers:
return func(*args, **kwargs)
else:
print('This host is not a production server, skipping function decorated with @production...')
return inner
def development(func: Callable):
def inner(*args, **kwargs):
if gethostname() not in production_servers:
return func(*args, **kwargs)
else:
print('This host is a production server, skipping function decorated with @development...')
return inner
def sit(func: Callable):
def inner(*args, **kwargs):
print('Skipping function decorated with @sit...')
return inner
@production
def foo():
print('Running in production, touching databases!')
foo()
@development
def foo():
print('Running in production, touching databases!')
foo()
@inactive
def foo():
print('Running in production, touching databases!')
foo()
简单的介绍一下这段代码。
在这里,先是罗列了生产环境的服务列表,然后分别定义了生产、开发、测试环境的装饰器,然后给同名的函数就可以配上对应的装饰器。
在执行代码的过程中,这段代码会首先获取hostname,自动判断所在环境,然后执行对应函数。
第三方工具包
上面是根据我们在开发过程中遇到的个性化场景进行来自定义一个装饰器。
作为一款以工具包著称的编程语言,Python中也有很多工具包提供了一些实用的装饰器。
以日志为例,这是每个程序员都无法绕开的。
调试程序对于大多数开发者来说是一项必不可少的工作,当我们想要知道代码是否按照预期的效果在执行时,我们会想到去输出一下局部变量与预期的进行比对。目前大多数采用的方法主要有以下几种:
Print函数
Log日志
IDE调试器
但是这些方法有着无法忽视的弱点:
繁琐
过度依赖工具
其中有一款不错的开源工具PySnooper就通过装饰器把这个问题巧妙的解决了。
PySnooper的调用方式就是通过@pysnooper.snoop的方式进行使用,该装饰器可以传入一些参数来实现一些目的,具体如下:
参数描述:
None输出日志到控制台
filePath输出到日志文件,例如'log/file.log'
prefix给调试的行加前缀,便于识别
watch查看一些非局部变量表达式的值
watch_explode展开值用以查看列表/字典的所有属性或项
depth显示函数调用的函数的snoop行
举个例子:
import numpy as np
import pysnooper
@pysnooper.snoop()
def one(number):
mat = []
while number:
mat.append(np.random.normal(0, 1))
number -= 1
return mat
one(3)
然后,就会给出如下输出:
Starting var:.. number = 3
22:17:10.634566 call 6 def one(number):
22:17:10.634566 line 7 mat = []
New var:....... mat = []
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 2
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746, -0.479901983375219]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 1
22:17:10.634566 line 8 while number:
22:17:10.634566 line 9 mat.append(np.random.normal(0, 1))
Modified var:.. mat = [-0.4142847169210746, -0.479901983375219, 1.0491540468063252]
22:17:10.634566 line 10 number -= 1
Modified var:.. number = 0
22:17:10.634566 line 8 while number:
22:17:10.634566 line 11 return mat
22:17:10.634566 return 11 return mat
Return value:.. [-0.4142847169210746, -0.479901983375219, 1.0491540468063252]
局部变量值、代码片段、局部变量所在行号、返回结果等,这些关键信息都输出了,既方便,又清晰。
内置
除了自定义和第三方工具包之外,Python还内置了很多不错的装饰器,例如@abc.abstractmethod、@asyncio.coroutine、@classmethod等等。
这里着重提一个非常强大的装饰器,能够极大的提升Python的运行速度和效率,通过一个装饰器能够将Python代码的执行速度提升上万倍,这个装饰器就是@functools.lru_cache。
以比较知名的斐波那契数列的例子来演示一下。
由于它递归计算的过程中,还会用到之前计算的结果,因此会涉及较多的重复计算,下面先看一下正常计算的耗时情况。
import time as tt
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
t1 = tt.time()
fib(30)
print("Time taken: {}".format(tt.time() - t1))
# 0.2073
n等于30时,耗时0.2073。
加上@functools.lru_cache装饰器再看一下:
import time as tt
import functools
@functools.lru_cache(maxsize=5)
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
t1 = tt.time()
fib(30)
print("Time taken: {}".format(tt.time() - t1))
# 1.811981e-05
耗时为1.811981e-05,足足差了4个量级,快了10000+倍!
来源:https://blog.csdn.net/weixin_56659172/article/details/125658212
猜你喜欢
- Liwu_Items表,CreateTime列建立聚集索引 第一种,sqlserver2005特有的分页语法 代码如下:declare @p
- 二进制转字符串入口参数:字节流函数返回:字符串Code By:Madpolice利用 ADODB.Stream 对象,速度比原来
- SQL Server有几个版本都在使用中——4.2, 6.0, 6.5, 7.0, 2000,以及2
- 怎么样才能设计出漂亮的网页?怎么样才能做好网页设计工作,现在许多人还停留在网页制作的水平上,认为只要用好了网页制作软件,就能搞好网页设计了,
- Firefox 3 有一个很让人讨厌的bug:基于某种目的,在表单提交时 disable 掉提交按钮,通过后退键回到这个页面后,这个提交按钮
- PHP 5.0.0 和PHP 4.0.38 于2004年7月13日同时发布,这是一个值得我们PHP爱好者的一大喜讯。期盼已久的PHP5终于出
- 写在之前我们都知道 Python 中内置了许多标准的数据结构,比如列表,元组,字典等。与此同时标准库还提供了一些额外的数据结构,我们可以基于
- 本文实例讲述了golang基于websocket实现的简易聊天室。分享给大家供大家参考,具体如下:先说点无关的,最近忙于工作没有更新博客,今
- 代码实现:<!DOCTYPE html><html lang="en"><head>
- JavaScript中的XMLHttpRequest和XML DOM首先,我们需要建立一些规则。特殊的XMLHttpRequest对象和一般
- 第二次遇到FF下正则兼容性问题( 第一次是关于"g"全局标志的,现在网上已有很多相关文章介绍)。以下正则在FF和IE下的
- 1、shutdown normal 正常方式关闭数据库。 2、shutdown imme
- 用VBS语言实现的一个简单网页计算器,功能:可以进行加法、减法、乘法、除法、取反、开根号、及指数运算。虽然简单但是比起windows xp自
- 在我们有时需要迁移或部署项目时,需要知道项目所依赖的三方包和版本,下面就来一看一看该如何获取吧:1、首先安装pipreqs库使用pip命令,
- 1.什么是搜索引擎?搜索引擎是“对网络信息资源进行搜集整理并提供信息查询服务的系统,包括信息搜集、信息整理和用户查询三部分”。如图1是搜索引
- 1. 需要的库, redispip install redis2. 连接Redisimport redisclass RedisCtrl(o
- 我设了两个SESSION:SESSION(A1),SESSION(A2),然后我现在想结束其中一个SESSION(如:ESEEION(A1)
- 小的本身是一个平面设计人员,前一阵儿有一些空闲的时间,便在各个站长网上发布了贴子,大意是免费制作logo,以换取网站连接(相信很多人都看过)
- 你也许已经掌握了id、class、后台选择器这些基本的css选择器。但这远远不是css的全部。下面向大家系统的解析css中30个最常用的选择
- 长话短说,今天介绍实现此功能的一个方法,需要了解的朋友可以参考下:一、JS 重载页面,本地刷新,返回上一页 代码如下:<a href=