Python装饰器可以实现什么,还有什么有趣的用法

Admin 2022-07-25 群英技术资讯 286 次浏览

这篇文章将为大家详细讲解有关“Python装饰器可以实现什么,还有什么有趣的用法”的知识,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

    

装饰器的价值不言而喻,可以用来增强函数功能、简化代码、减少代码冗余。

它的使用场景同样很多,比较简单的场景包含打印日志、统计运行时间,这类例子和用法网上已经很多了:

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+倍!


感谢各位的阅读,以上就是“Python装饰器可以实现什么,还有什么有趣的用法”的内容了,经过本文的学习后,相信大家对Python装饰器可以实现什么,还有什么有趣的用法都有更深刻的体会了吧。这里是群英网络,小编将为大家推送更多相关知识点的文章,欢迎关注! 群英智防CDN,智能加速解决方案
标签: python装饰器

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。

猜你喜欢

成为群英会员,开启智能安全云计算之旅

立即注册
专业资深工程师驻守
7X24小时快速响应
一站式无忧技术支持
免费备案服务
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
在线客服
微信公众号
返回顶部
返回顶部 返回顶部
在线客服
在线客服