python decorator模块_学习python decorator模块

本文简介

decorator模块是 Michele Simionato 为简化python的decorator的使用难度而开发的,使用它,您可以更加容易的使用decorator机制写出可读性、可维护性更好的代码。

本文大部分翻译自下面这篇文档: www.phyast.pitt.edu/~micheles/python/documentation.html , 之中或会加入自己的理解或注释

decorator 模块

简介

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通常是不能够组合使用的(如staticmethod和classmethod)。

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

问题的描述

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

python 代码

try:

fromfunctoolsimportupdate_wrapper

exceptImportError:# using Python version 

defdecorator_trace(f):

defnewf(*args, **kw):

print"calling %s with args %s, %s" % (f.__name__, args, kw)

returnf(*args, **kw)

newf.__name__= f.__name__

newf.__dict__.update(f.__dict__)

newf.__doc__= f.__doc__

newf.__module__ = f.__module__

returnnewf

else:# using Python 2.5+

defdecorator_trace(f):

defnewf(*args, **kw):

print"calling %s with args %s, %s" % (f.__name__, args, kw)

returnf(*args, **kw)

returnupdate_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 代码

deftrace(f, *args, **kw):

print"calling %s with args %s, %s" % (f.func_name, args, kw)

returnf(*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 with args (), {}

下面开始讨论decorators的用法。

缓存化(memoize)

这里讨论的decorator实现了memoize模式,它可以把函数调用结果存储在一个字典对象中,下次使用相同参数调用该函数时,就可以直接从该字典对象里面获取结果而无需重新计算。

memoize 代码

fromdecoratorimport*

defgetattr_(obj, name, default_thunk):

"Similar to .setdefault indictionaries."

try:

returngetattr(obj, name)

exceptAttributeError:

default = default_thunk()

setattr(obj, name, default)

returndefault

@decorator

defmemoize(func, *args):

dic = getattr_(func, "memoize_dic", dict)

# memoize_dic is created at the first call

ifargsindic:

returndic[args]

else:

result = func(*args)

dic[args] = result

returnresult

下面时使用测试:

>>> @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的实现

importthreading

@decorator

deflocked(func, *args, **kw):

lock = getattr_(func, "lock", threading.Lock)

lock.acquire()

try:

result = func(*args, **kw)

finally:

lock.release()

returnresult

锁的使用

importtime

datalist = [] # for simplicity the written data are stored into a list.

@locked

defwrite(data):

"Writing to a sigle-access resource"

time.sleep(1)

datalist.append(data)

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

分享到:

18e900b8666ce6f233d25ec02f95ee59.png

72dd548719f0ace4d5f9bca64e1d7715.png

2007-04-15 23:39

浏览 6220

评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值