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装饰器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 巧用Python装饰器 免去调用父类构造函数的麻烦

    先看一段代码: 复制代码 代码如下: class T1(threading.Thread): def __init__(self, a, b, c): super(T1, self).__init__() self.a = a self.b = b self.c = c def run(self): print self.a, self.b, self.c 代码定义了一个继承自threading.Thread的class,看这句 super(T1, self).__init__() 也有些人喜欢

  • Python装饰器的函数式编程详解

    Python的装饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西.虽然好像,他们要干的事都很相似--都是想要对一个已有的模块做一些"修饰工作",所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去.但是OO的Decorator简直就是一场恶梦,不信你就去看看wikipedia上的词条

  • 基于Python 装饰器装饰类中的方法实例

    title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] category: ['Python'] --- 目前在中文网上能搜索到的绝大部分关于装饰器的教程,都在讲如何装饰一个普通的函数.本文介绍如何使用Python的装饰器装饰一个类的方法,同时在装饰器函数中调用类里面的其他方法.本文以捕获一个方法的异常为例来进行说明. 有一个类Test, 它的结构如下: clas

  • python函数装饰器之带参数的函数和带参数的装饰器用法示例

    本文实例讲述了python函数装饰器之带参数的函数和带参数的装饰器用法.分享给大家供大家参考,具体如下: 1. 函数带多个参数 # 普通的装饰器, 打印函数的运行时间 def decrator(func): def wrap(*args, **kwargs): start_time = time.time() res = func(*args, **kwargs) end_time = time.time() print('运行时间为', end_time-start_time) return

  • python装饰器简介---这一篇也许就够了(推荐)

    Python装饰器(decorator)是在程序开发中经常使用到的功能,合理使用装饰器,能让我们的程序如虎添翼. 装饰器引入 初期及问题诞生 假如现在在一个公司,有A B C三个业务部门,还有S一个基础服务部门,目前呢,S部门提供了两个函数,供其他部门调用,函数如下: def f1(): print('f1 called') def f2(): print('f2 called') 在初期,其他部门这样调用是没有问题的,随着公司业务的发展,现在S部门需要对函数调用假如权限验证,如果有权限的话,才

  • python 装饰器功能以及函数参数使用介绍

    简单的说:装饰器主要作用就是对函数进行一些修饰,它的出现是在引入类方法和静态方法的时候为了定义静态方法出现的.例如为了把foo()函数声明成一个静态函数 复制代码 代码如下: class Myclass(object): def staticfoo(): ............ ............ staticfoo = staticmethod(staticfoo) 可以用装饰器的方法实现: 复制代码 代码如下: class Myclass(object): @staticmethod

  • Python中的各种装饰器详解

    Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: 复制代码 代码如下: >>> def test(func):     def _test():         print 'Call the function %s().'%func.func_name         return func()     return _test >

  • python 一篇文章搞懂装饰器所有用法(建议收藏)

    01. 装饰器语法糖 如果你接触 Python 有一段时间了的话,想必你对 @ 符号一定不陌生了,没错 @ 符号就是装饰器的语法糖. 它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上.和这个函数绑定在一起.在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶上这顶帽子,这顶帽子我们称之为装饰函数 或 装饰器. 你要问我装饰器可以实现什么功能?我只能说你的脑洞有多大,装饰器就有多强大. 装饰器的使用方法很固定: 先定义一个装饰函数(帽子)(也可以

  • Python中的装饰器用法详解

    本文实例讲述了Python中的装饰器用法.分享给大家供大家参考.具体分析如下: 这里还是先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: 复制代码 代码如下: @makebold @makeitalic def say():    return "Hello" 打印出如下的输出: <b><i>Hello<i></b> 你会怎么做?最后给出的答案是: 复制代码 代码如下: def makebold(fn):    

  • python中多个装饰器的执行顺序详解

    装饰器是程序开发中经常会用到的一个功能,也是python语言开发的基础知识,如果能够在程序中合理的使用装饰器,不仅可以提高开发效率,而且可以让写的代码看上去显的高大上^_^ 使用场景 可以用到装饰器的地方有很多,简单的举例如以下场景 引入日志 函数执行时间统计 执行函数前预备处理 执行函数后清理功能 权限校验等场景 缓存 今天讲一下python中装饰器的执行顺序,以两个装饰器为例. 装饰器代码如下: def wrapper_out1(func): print('--out11--') def i

  • Python装饰器限制函数运行时间超时则退出执行

    实际项目中会涉及到需要对有些函数的响应时间做一些限制,如果超时就退出函数的执行,停止等待. 可以利用python中的装饰器实现对函数执行时间的控制. python装饰器简单来说可以在不改变某个函数内部实现和原来调用方式的前提下对该函数增加一些附件的功能,提供了对该函数功能的扩展. 方法一. 使用 signal # coding=utf-8 import signal import time def set_timeout(num, callback): def wrap(func): def h

随机推荐