python decorator模块_学习python decorator模块

python decorator模块_学习python decorator模块本文简介 decorator 模块是 MicheleSimio 为简化 python 的 decorator 的使用难度而开发的 使用它 您可以更加容易的使用 decorator 机制写出可读性 可维护性更好的代码 本文大部分翻译自下面这篇文档 www phyast pitt edu micheles python documentatio html 之中或会加入自己的理解或注释 decorat

本文简介

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

评论

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228703.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午6:24
下一篇 2026年3月16日 下午6:24


相关推荐

  • catic备份mysql,Catic构建与部署

    catic备份mysql,Catic构建与部署前置提示 注意 如果你是 centos7 以上环境 你用 yum 方式安装的 mysql 不在是 mysql 而是 MariaDB 因为 centos7 开始已经很嫌弃 mysql 所以安装后你的数据库会是 MariaDBMySQL 在 CentOS7 版本后改名为 MariaDBMaria 启动 停止 重启命令启动 MariaDB 命令 systemctlsta service 停止 MariaDB 命

    2026年3月19日
    1
  • GPG error 解决方案「建议收藏」

    GPG error 解决方案「建议收藏」错误提示:GPG错误:http://mirrors.163.commaverick-updatesRelease:下列签名无效:BADSIG40976EAF437D05B5UbuntuArchiveAutomaticSigningKey@ubuntu.com>修复方法gp

    2022年10月13日
    3
  • 统计分析SQL Server Profiler 跟踪的SQL

    统计分析SQL Server Profiler 跟踪的SQL

    2021年12月4日
    58
  • 外链式样式表_引入CSS样式表(书写位置)

    外链式样式表_引入CSS样式表(书写位置)CSS初识CSS(CascadingStyleSheets)美化样式CSS通常称为CSS样式表或层叠样式表(级联样式表),主要用于设置HTML页面中的文本内容(字体、大小、对齐方式等)、图片的外形(宽高、边框样式、边距等)以及版面的布局等外观显示样式。CSS以HTML为基础,提供了丰富的功能,如字体、颜色、背景的控制及整体排版等,而且还可以针对不同的浏览器设置不同的样式。引入CSS样式表(书…

    2022年7月14日
    20
  • 搞清clientHeight、offsetHeight、scrollHeight、offsetTop、scrollTop

    搞清clientHeight、offsetHeight、scrollHeight、offsetTop、scrollTop转载自:https://www.imooc.com/article/17571网页可见区域高:document.body.clientHeight网页正文全文高:document.body.scrollHeight网页可见区域高(包括边线的高):document.body.offsetHeight网页被卷去的高:document.body.scrollTop屏幕分辨率高:window.screen…

    2022年7月24日
    17
  • LaTeX:引号

    LaTeX:引号LaTeX;LaTeX引号;LaTeX单引号;LaTeX双引号。

    2022年6月25日
    53

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号