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
猜你喜欢
- Python的turtle模块画国旗主要用到两个函数:draw_rentangle和draw_star。至于函数的调用就和我们学的C,C++
- 在使用Tkinter做界面时,遇到这样一个问题:程序刚运行,尚未按下按钮,但按钮的响应函数却已经运行了例如下面的程序:from Tkinte
- 1.requests库简介requests 是 Python 中比较常用的网页请求库,主要用来发送 HTTP 请求,在使用爬虫或测试服务器响
- 遍历并修改图像像素值在使用opencv处理图像时,有时需要对图像的每个像素点进行处理,比如取反、修改值等操作,就需要通过h和w遍历像素。依然
- PHP 异常处理异常用于在指定的错误发生时改变脚本的正常流程。异常是什么异常处理用于在指定的错误(异常)情况发生时改变脚本的正常流程。这种情
- 在上一讲代码的基础上,做进一步修改,成为了如下程序,请看官研习这个程序:#!/usr/bin/env python#coding:utf-8
- 文本框: <input type="text" onkeydown="keydownsearch(eve
- 如下所示:$preg= '/xue[\s\S]*?om/i';preg_match_all($preg,"学并思网
- 一、模拟登录图书馆管理系统我们可以先看一下登录页面(很多学校这些管理系统页面就是很low):两种方式去模拟登录图书馆:1. 构造登录表单进行
- 快到 520 了,分享几段 520 专属 Python 代码,不多说了,下面直接上货。No.1效果:主要代码:import tur
- 本文实例为大家分享了python使用turtle库绘制树的具体代码,供大家参考,具体内容如下# -*- coding: utf-8 -*-&
- python数值与字符串高级用法1.概述这篇是一篇没有尽头的文章,每当过段时间,再次打开就会看到不一样的内容,有新东西在更新啊。是啊,之所以
- Python是一门简单易学的编程语言。阅读好的Python程序感觉就像阅读英语,尽管是非常严格的英语。Python的这种伪代码特性是其最大强
- 使用:foldercleanup.py -d 10 -k c:\test\keepfile.txt c:\test表示对c:\test目录只
- 把 Oracle 数据库从 RAC 集群迁移到单机环境一、系统环境1、源数据库db_name:hisdb SID:hisdb1、
- 本文实例讲述了PHP实现的MD5结合RSA签名算法。分享给大家供大家参考,具体如下:<?phpclass Md5RSA{
- 本文实例讲述了python实现的AES双向对称加密解密与用法。分享给大家供大家参考,具体如下:高级加密标准(Advanced Encrypt
- 爬虫思路初步尝试我先查看了network,并没有发现有可用的API;然后又用bs4去分析英雄列表页,但是请求到html里面,并没有英雄列表,
- 1.panic是什么panic() 是一个内建的函数2.panic有什么用作用:可以使用panic停止程序继续运行,所以大多数都是在demo
- 准备工作1.安装scrapy_redis包,打开cmd工具,执行命令pip install scrapy_redis2.准备好一个没有BUG