`
hfeeqi
  • 浏览: 13930 次
  • 性别: 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==4.4.2

    在"python decorator==4.4.2"版本中,我们可以探讨这个库提供的装饰器功能及其在Odoo中的应用。 首先,让我们理解什么是Python装饰器。装饰器本质上是一个接收函数作为参数并返回新函数的函数。通过在定义函数前...

    python实现Decorator模式实例代码

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

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

    这个"decorator python"模块是版本3.4.0的实现,其核心概念是通过函数来包装(即装饰)其他函数,以增强被装饰函数的行为。这个模块可能是由社区成员贡献并分享的,因此它属于"其他资源"类别。 在Python中,装饰器...

    Python函数模块祥例.zip

    本资源"Python函数模块祥例.zip"提供了丰富的实例,帮助初学者深入理解Python函数模块的使用,以及如何巧妙地运用匿名函数(lambda函数)。 首先,我们要了解Python中的基础函数概念。函数通过`def`关键字定义,...

    Python基础入门教程 Python语言编程导论05 第五章 模块与函数 (共110页).rar

    此外,Python的装饰器(Decorator)是一种高级函数,可以修改或增强其他函数的功能,而不改变其原始代码。装饰器在处理日志、性能测试、事务处理等方面非常有用。 总的来说,理解并熟练运用Python的模块和函数是...

    python中完善decorator共5页.pdf.zip

    Python中的装饰器(Decorator)是该编程语言的一个强大特性,它允许我们修改或增强函数...通过学习上述知识点,并结合提供的压缩文件内容,你可以深入理解Python装饰器的工作原理和实际应用,提升你的Python编程技能。

    python中编写无参数decorator共4页.pdf

    在Python的装饰器中,`functools` 模块提供了一些有用的工具,如 `@wraps`,它可以用来保持被装饰函数的元信息(如函数名、文档字符串和属性)。例如: ```python from functools import wraps def decorator...

    Python 学习

    Python中的模块和包管理使得代码复用和组织变得容易,比如导入numpy进行数值计算,matplotlib进行数据可视化。 描述中提到的"第一本入门书"通常会包含上述基础知识,并逐步引导学习者理解Python的高级特性。例如,...

    装饰器decorator_python_

    装饰器在Python编程中是一种强大...通过`logit.py`和`decorator.py`中的示例,我们可以学习如何利用装饰器实现日志记录、参数控制等多种实用功能。在实际开发中,合理地运用装饰器能够显著提高代码的可读性和可维护性。

    python基础课程学习资料.zip

    此外,可能会讲解Python的模块导入机制,如何使用import语句以及from...import语法,以及如何创建自定义模块。 5. **05函数** 函数是Python编程的基础,本章可能深入介绍了函数的定义、参数传递(位置参数、关键字...

    Python教程入门到精通:千锋Python语法基础学习视频教程.pdf

    在学习Python的过程中,理解并掌握其语法是至关重要的,因为它是后续深入学习和应用的基础。 **Python基础教程内容概览** 1. **第一个Python程序和数据存储**:这部分将介绍如何编写和运行Python的第一个程序,...

    记录自己学习 python 的过程。.zip

    Python还支持匿名函数,即lambda函数,以及装饰器(decorator),用于函数的增强或封装。 模块化编程让项目组织更加有序。Python的模块系统允许我们将代码分割到不同的文件中,通过`import`语句引入。标准库提供了...

    廖雪峰《Python 教程》学习笔记

    6. **高级特性**:Python还有许多高级特性,如生成器(generator)、装饰器(decorator)、上下文管理器(with语句)和元编程。这些特性让Python在处理复杂问题时显得更为灵活高效。 在学习过程中,结合...

    [Python学习手册(第4版)].源代码

    《Python学习手册(第4版)》的源代码包含了丰富的Python编程知识,是学习和深入理解Python语言的重要资源。此套源代码旨在帮助读者通过实际的示例来探索Python的各种特性和用法,涵盖从基础语法到高级概念的广泛内容...

    A Byte of Python3 附源代码

    Python中的装饰器(decorator)是一种高级技巧,它们可以用来修改或增强函数的行为。 异常处理是任何编程语言中不可或缺的部分,Python 3使用try/except语句来捕获和处理异常。同时,Python 3引入了with语句,方便...

    python中编写带参数decorator共5页.pdf

    总的来说,Python中的带参数装饰器是实现代码复用和模块化的重要手段,它们能够帮助我们编写更加灵活且易于维护的程序。理解并熟练掌握带参数装饰器的使用,对于提升Python编程技能和解决实际问题具有重要意义。

    python基础学习笔记

    Python还有许多其他高级特性,如装饰器(decorator)用于修改函数行为,生成器(generator)用于实现惰性计算和节省内存,上下文管理器(context manager)通过with语句简化资源管理。 "Readme.txt"文件可能是对学习笔记...

    python速查表和全套手册.rar

    这份“Python速查表和全套手册”是学习和提升Python技能的宝贵资源,尤其适合初学者进行查阅和参考。 速查表通常包含常用语法、函数、模块以及快捷键等关键信息,便于开发者在编写代码时快速查找和记忆。Python速查...

    Python装饰器模式学习demo

    这种设计模式在Python中非常常见,因为它提供了一种灵活的方式来扩展功能,保持代码的整洁和模块化。 在"Python装饰器学习demo"中,我们可以探索以下几个关键知识点: 1. **基础装饰器**:装饰器的基本形式是一个...

    python学习中的一些小练习.zip

    学习者可能会遇到如何定义函数、传递参数、返回值、局部和全局变量的区别,以及装饰器(decorator)的概念。 4. **类与对象**:面向对象编程是Python的重要特性,练习可能包含定义类、继承、封装、多态以及对象的...

Global site tag (gtag.js) - Google Analytics