`
hfeeqi
  • 浏览: 13777 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

学习python decorator模块

阅读更多
本文简介
decorator模块是 Michele Simionato 为简化python的decorator的使用难度而开发的,使用它,您可以更加容易的使用decorator机制写出可读性、可维护性更好的代码。
本文大部分翻译自下面这篇文档: www.phyast.pitt.edu/~micheles/python/documentation.html , 之中或会加入自己的理解或注释

decorator 模块


作者: E-mail: 版本: 下载: 网络安装: License:
Michele Simionato
michele.simionato@gmail.com
2.0.1
http://www.phyast.pitt.edu/~micheles/python/decorator-2.0.1.zip
easy_install decorator
Python license

简介

Python 2.4 的 decorators 是一个展现语法糖的有趣的例子: 原理上, 它们的引入没有改变任何东西,因为它们没有提供任何新的在原语言里面不存在的功能; 实践中, 它们的引入能够显著的改善我们的Python代码结构. 我相信这种改变是出于好意的, 基于以下理由,decorators是一些非常好的想法:
  • decorators 帮助减少教条化/样板化的代码;
  • decorators 有助于分离关注点
  • decorators 有助于增强可读性和可维护性
  • decorators 是非常直接/清晰的
但是,到现在为止(译者注: 到Python 2.5 里面已经有所改善),正确的定制一个decorators需要一些经验, 它比我们想像的要难于使用。例如,典型的decorators的实现涉及到嵌套(nested)函数,而我们都知道:flat 比 nested 好。

decorator的目的是为普通开发人员简化dccorators的使用,并且通过一些有用的例子来推广decorators的使用,例如:memoize,tracing,redirecting_stdout, locked等等。

模块的核心是一个叫做decorator的decorator工厂函数。所有在这里讨论的简单的decorators解决方案都是基于decorator模块的。通过执行如下命令后产生的 _main.py 文件中包含了这些所有的这些代码:
> python doctester.py documentation.txt
同时,该命令还会运行所有的作为测试用例存在的例子。

定义

从技术上来讲, 任何可以被调用的、带一个参数的Python对象都可以被当作是一个decorator。 然而,这个定义因为过于宽泛而显得并无真正得用处。为方便起见,我们可以把decorator分为两类:
  • 保留签名的(signature-preserving)decorators,例如:将一个函数A作为可调用对象(decorators)的输入并返回一个函数B,函数A和B的签名是相同的;
  • 改变签名的(signature-changing)decorators, 例如:将一个函数A作为可调用对象(decorators)的输入并返回一个函数B,函数A和B的签名是不相同的,或者该decorator的返回值就不是一个可调用对象。

Signature-changing decorators有它们的用处,例如:内部类 staticmethod classmethod 就属于这一类,因为它们接受函数作为输入并返回一个descriptor对象,这个对象不是函数,也不是可调用对象。

但是,signature-preserving decorators 则更加通用,更加容易理解,尤其是这一类decorators能够被组合起来使用,而其他decorators通常是不能够组合使用的(如staticmethodclassmethod)。

从零开始写一个signature-preserving decorator 不是那么浅显的,尤其是当你想实现一个正确的能够接受任意签名的decorator时,一个简单的例子将阐明这个问题。

问题的描述

假设你想跟踪一个函数的执行:这是一个常见的使用decorator的例子,很多地方你都能看到类似的代码

python 代码
  1. try:  
  2.     from functools import update_wrapper  
  3. except ImportError# using Python version < 2.5  
  4.     def decorator_trace(f):  
  5.         def newf(*args, **kw):  
  6.            print "calling %s with args %s, %s" % (f.__name__, args, kw)  
  7.            return f(*args, **kw)  
  8.         newf.__name__ = f.__name__  
  9.         newf.__dict__.update(f.__dict__)  
  10.         newf.__doc__ = f.__doc__  
  11.         newf.__module__ = f.__module__  
  12.         return newf  
  13. else# using Python 2.5+  
  14.     def decorator_trace(f):  
  15.         def newf(*args, **kw):  
  16.             print "calling %s with args %s, %s" % (f.__name__, args, kw)  
  17.             return f(*args, **kw)  
  18.         return update_wrapper(newf, f)  

(译者注:上面的代码中虽然有修改结果对象的自省信息(或元信息),但是修改得不全面,如其签名信息就没有修改为与被修饰对象保持一致)
上面代码是想实现一个能够接受一般签名的decorator,不幸的是,该实现并没有定义为一个signature-preserving的decorator,因为大体上说 decorator_trace 返回了一个与被修饰函数不同的签名。
思考下面的例子:

>>> @decorator_trace
... def f1(x):
... pass

这里原先的函数只接受一个参数,而修饰后的函数却接受任意的参数和关键字参数。
>>> from inspect import getargspec
>>> print getargspec(f1)
([], 'args', 'kw', None)

这就意味这自省工具如:pydoc将会给出关于函数f1的错误的签名信息,这是一个美丽的错误:pydoc将告诉你该函数能够接受一个通用的签名:*args, **kw,但是当你使用超过一个的参数去调用该函数时,你将会得到如下错误:
>>> f1(0, 1)
Traceback (most recent call last):
...
TypeError: f1() takes exactly 1 argument (2 given)


解决方案

这个方案提供了一个通用的 decorators  工厂,它对应用程序员隐藏了实现 signature-preserving  的 decorator 的复杂性。该工厂允许在不使用嵌套函数或嵌套类的情况下定义decorators, 下面是一个告诉你怎样定义 decorator_trace 的简单例子。
首先,导入decorator模块:
>>> from decorator import decorator
然后定义一个辅助函数f,该函数具有如下signature:(f, *args, **kw),该函数只是简单的做如下调用f(args, kw):
python 代码
 
  1. def trace(f, *args, **kw):  
  2.     print "calling %s with args %s, %s" % (f.func_name, args, kw)  
  3.     return f(*args, **kw)  

decorator 模块能够把帮助函数转变成一个 signature-preserving 对象,例如:一个接受一个函数作为输入的可调用对象,并返回一个被修饰过的函数,该函数具有与原函数一致的签名,这样,你就能够这样写:
>>> @decorator(trace)
... def f1(x):
... pass
很容易就验证函数 f1 正常工作了
>>> f1(0)
calling f1 with args (0,), {}
并且它具有正确的签名:
>>> print getargspec(f1)
(['x'], None, None, None)
同样的,该decorator对象对具有其他签名的函数也是有效的:
>>> @decorator(trace)
... def f(x, y=1, z=2, *args, **kw):
... pass
>>> f(0, 3)
calling f with args (0, 3, 2), {}
>>> print getargspec(f)

甚至包括下面这些具有奇特签名的函数也能够正常工作:
>>> @decorator(trace)
... def exotic_signature((x, y)=(1,2)): return x+y

>>> print getargspec(exotic_signature)
([['x', 'y']], None, None, ((1, 2),))
>>> exotic_signature()
calling exotic_signature with args ((1, 2),), {}
3

(['x', 'y', 'z'], 'args', 'kw', (1, 2))

decorator is a Decorator


工厂函数decorator本身就可以被当作是一个 signature-changing 的decorator, 就和 classmethod 和 staticmethod 一样,不同的是这两个内部类返回的是一般的不可调用的对象,而decorator返回的是 signature-preserving 的decorators, 例如带单参数的函数。这样,你能够这样写:
>>> @decorator
... def tracing(f, *args, **kw):
... print "calling %s with args %s, %s" % (f.func_name, args, kw)
... return f(*args, **kw)
 这中惯用法实际上是把 tracing 重定义为一个 decorator , 我们能够很容易的检测到tracing的签名被更改了:
>>> print getargspec(tracing)
(['f'], None, None, None)
这样,tracing能够被当作一个decorator使用,下面的代码能够工作:
>>> @tracing
... def func(): pass
>>> func()
calling func with args (), {}
BTW,你还能够对 lambda 函数应用该 decorator:
>>> tracing(lambda : None)()
calling <lambda> with args (), {}</lambda>
下面开始讨论decorators的用法。

缓存化(memoize)


这里讨论的decorator实现了memoize模式,它可以把函数调用结果存储在一个字典对象中,下次使用相同参数调用该函数时,就可以直接从该字典对象里面获取结果而无需重新计算。
memoize 代码
 
  1. from decorator import *  
  2.   
  3. def getattr_(obj, name, default_thunk):  
  4.     "Similar to .setdefault in dictionaries."  
  5.     try:  
  6.         return getattr(obj, name)  
  7.     except AttributeError:  
  8.         default = default_thunk()  
  9.         setattr(obj, name, default)  
  10.         return default  
  11.   
  12. @decorator  
  13. def memoize(func, *args):  
  14.     dic = getattr_(func, "memoize_dic", dict)  
  15.     # memoize_dic is created at the first call  
  16.     if args in dic:  
  17.         return dic[args]  
  18.     else:  
  19.         result = func(*args)  
  20.         dic[args] = result  
  21.         return result  
下面时使用测试:
>>> @memoize
... def heavy_computation():
... time.sleep(2)
... return "done"
>>> print heavy_computation() # the first time it will take 2 seconds
done
>>> print heavy_computation() # the second time it will be instantaneous
done
作为练习, 您可以尝试不借助于decorator工厂来正确的实现memoize。
注意:这个memoize实现只有当函数没有关键字参数时才能够正常工作,因为实际上不可能正确的缓存具有可变参数的函数。您可以放弃这个需求,允许有关键字参数存在,但是,当有关键字参数被传入时,其结果是不能够被缓存的。具体例子请参照http://www.python.org/moin/PythonDecoratorLibrary

锁(locked)


不想再翻这一小节了!这一小节本来已经翻译了,但是由于系统故障导致被丢失了,而且因为Python2.5中已经实现了with语句,因此这一小节的内容也就显得不是那么重要了。
代码附上:
locked的实现
 
  1. import threading  
  2.   
  3. @decorator  
  4. def locked(func, *args, **kw):  
  5.     lock = getattr_(func, "lock", threading.Lock)  
  6.     lock.acquire()  
  7.     try:  
  8.         result = func(*args, **kw)  
  9.     finally:  
  10.         lock.release()  
  11.     return result  

锁的使用
 
  1. import time  
  2.   
  3. datalist = [] # for simplicity the written data are stored into a list.  
  4.   
  5. @locked  
  6. def write(data):  
  7.     "Writing to a sigle-access resource"  
  8.     time.sleep(1)  
  9.     datalist.append(data)  

延时化和线程化(delayed and threaded)


分享到:
评论

相关推荐

    python实现Decorator模式实例代码

    本文研究的主要是python实现Decorator模式,具体介绍如下。 一般来说,装饰器是一个函数,接受一个函数(或者类)作为参数,返回值也是也是一个函数(或者类)。首先来看一个简单的例子: # -*- coding: utf-8 -*- ...

    decorator python(decorator-3.4.0.tar.gz).rar

    软件介绍: 这个decorator python是网友分享过来的一个模块文件,既然下载肯定会知道它的用途。

    Python使用logging结合decorator模式实现优化日志输出的方法

    本文实例讲述了Python使用logging结合decorator模式实现优化日志输出的方法。分享给大家供大家参考,具体如下: python内置的loging模块非常简便易用, 很适合程序运行日志的输出。 而结合python的装饰器模式,则可...

    python面试题目-python-python经典面试题目-Python语言的基本概念-常用的功能和特性-编程范式-面试题目

    Python中的模块(Module)和包(Package)有什么区别? Python中如何进行文件读写操作? 什么是Python中的装饰器(Decorator)?如何使用装饰器? Python中如何处理日期和时间? 什么是Python中的Lambda函数?...

    unread-decorator:Python 模块允许 unread() 从任何流中 read() 的数据

    1 add_unread 的用法 from unread_decorator import add_unreadf = add_unread ( open ( filename , mode , buffering ))# assume f contains "one\ntwo\nthree"f = add_unread ( f ) # decoratedata = f ....

    python-autocast-decorator:用于自动将字符串输入转换为其最可能的 Python 数据类型的装饰器

    python-autocast-decorator 用于自动将字符串输入转换为最可能的 Python 数据类型的装饰器。 此实现在所有输入上运行ast.literal_eval&#40;&#41; 。 这简单可靠,但速度相当慢,因此可能不适合必须快速运行的代码...

    func_timeout:Python模块,允许您在调用任何现有函数时指定超时,并支持可停止线程

    Python模块支持以给定的超时时间运行任何现有功能。 功能超时 func_timeout 这是您传递超时,要调用的函数以及所有参数的函数,并且它最多运行#timeout#秒,并且将返回/引发传递的函数否则将返回或引发的所有...

    automain:一个简单的 Python 模块,在加载时自动执行函数

    自动主 使用语法的更简洁的快捷方式: def mymain(): print 'this is our main... from automain import * # will only import the automain decorator @automain def mymain(): print 'this is our main function'

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

    Python的装饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西。虽然好像,他们要干的事都很相似——都是想要对一个已有的模块做...

    Python 工匠:使用装饰器的技巧

    装饰器(Decorator) 是 Python 里的一种特殊工具,它为我们提供了一种在函数外部修改函数的灵活能力。它有点像一顶画着独一无二 @ 符号的神奇帽子,只要将它戴在函数头顶上,就能悄无声息的改变函数本身的行为。 你...

    MoviePy:使用 Python 编辑视频-开源

    MoviePy 依赖于 Python 模块 NumPy、Imageio、Decorator 和 Proglog,它们将在 MoviePy 安装过程中自动安装。 在您第一次使用 MoviePy 时,软件 FFMPEG 应该会自动下载/安装(通过 imageio)(安装需要几秒钟)。

    python3-in-one-pic

    while break并continue 迭代器和发电机 理解力 功能 定义 争论 拉姆达 文献资料 @decorator 班级(OOP) class __init__()和self 实例 遗产 覆写 模块 import 搜索路径 包裹Pythonic标准库 os, sys datetime捐款...

    Python闭包和装饰器用法实例详解

    Python的装饰器的英文名叫Decorator,作用是完成对一些模块的修饰。所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块...

    装潢:装潢

    人类装饰 装饰器模块的目的是使定义签名保留功能的装饰器和装饰器工厂变得容易。...请注意,如果您的系统中存在较旧版本的decorator模块,则可能会遇到麻烦; 在这种情况下,请删除旧版本。 即使将模块decora

    modulepackage:PyCon2015 教程“模块和包”的材料

    模块和包:生与死 ... decorator_assembly/ :使用特殊的@export decorator_assembly/器从子模块组装一个包。 第 3 部分 -主要 main_wrapper :编写一个使用-m选项环绕脚本的模块的示例。 第 4 部分 - sys.pat

    Python装饰器实现几类验证功能做法实例

    之前用了一些登录验证的现成装饰器模块。然后仿写一些用户管理部分的权限装饰器。 比如下面这种 def permission_required(permission): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): ...

    topcoder-mm-decorator:处理和输出马拉松比赛站立

    必要的事情Python 2.7+依赖库使用pip或easy_install安装以下模块。 xml文件真子数学urllib2使用方法(示例) usage: tc_mm_decorater.py [-h] [-u] [-t REFRESH_TIME] [-r ROUND_ID] [-T TITLE] [-d DIR]第一次运行...

    python单例模式的多种实现方法

    在 Python 中,我们可以用多种方法来实现单例模式: 使用模块 使用 __new__ 使用装饰器(decorator) 使用元类(metaclass) 概念 简单说,单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,...

Global site tag (gtag.js) - Google Analytics