Python中用Decorator来简化元编程的教程


Posted in Python onApril 13, 2015

少劳多得

Decorator 与 Python 之前引入的元编程抽象有着某些共同之处:即使没有这些技术,您也一样可以实现它们所提供的功能。正如 Michele Simionato 和我在 可爱的 Python 专栏的早期文章 中指出的那样,即使在 Python 1.5 中,也可以实现 Python 类的创建,而不需要使用 “元类” 挂钩。

Decorator 根本上的平庸与之非常类似。Decorator 所实现的功能就是修改紧接 Decorator 之后定义的函数和方法。这总是可能的,但这种功能主要是由 Python 2.2 中引入的 classmethod() 和 staticmethod() 内置函数驱动的。在旧式风格中,您可以调用 classmethod(),如下所示:
清单 1. 典型的 “旧式” classmethod

class C:
  def foo(cls, y):
    print "classmethod", cls, y
  foo = classmethod(foo)

虽然 classmethod() 是内置函数,但并无独特之处;您也可以使用自己的方法转换函数。例如:
清单 2. 典型的 “旧式” 方法的转换

def enhanced(meth):
  def new(self, y):
    print "I am enhanced"
    return meth(self, y)
  return new
class C:
  def bar(self, x):
    print "some method says:", x
  bar = enhanced(bar)

decorator 所做的一切就是使您避免重复使用方法名,并且将 decorator 放在方法定义中第一处提及其名称的地方。例如:
清单 3. 典型的 “旧式” classmethod

class C:
  @classmethod
  def foo(cls, y):
    print "classmethod", cls, y
  @enhanced
  def bar(self, x):
    print "some method says:", x

decorator 也可以用于正则函数,采用的是与类中的方法相同的方式。令人惊奇的是,这一切是如此简单(严格来说,甚至有些不必要),只需要对语法进行简单修改,所有东西就可以工作得更好,并且使得程序的论证更加轻松。通过在方法定义的函数之前列出多个 decorator,即可将 decorator 链接在一起;良好的判断可以有助于防止将过多 decorator 链接在一起,不过有时候将几个 decorator 链接在一起是有意义的:
清单 4. 链接 decorator

@synchronized
@logging
def myfunc(arg1, arg2, ...):
  # ...do something
# decorators are equivalent to ending with:
#  myfunc = synchronized(logging(myfunc))
# Nested in that declaration order

Decorator 只是一个语法糖,如果您过于急切,那么它就会使您搬起石头砸了自己的脚。decorator 其实就是一个至少具有一个参数的函数 —— 程序员要负责确保 decorator 的返回内容仍然是一个有意义的函数或方法,并且实现了原函数为使连接有用而做的事情。例如,下面就是 decorator 两个不正确的用法:
清单 5. 没有返回函数的错误 decorator

>>> def spamdef(fn):
...   print "spam, spam, spam"
...
>>> @spamdef
... def useful(a, b):
...   print a**2 + b**2
...
spam, spam, spam
>>> useful(3, 4)
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
TypeError: 'NoneType' object is not callable

decorator 可能会返回一个函数,但这个函数与未修饰的函数之间不存在有意义的关联:
清单 6. 忽略传入函数的 decorator

>>> def spamrun(fn):
...   def sayspam(*args):
...     print "spam, spam, spam"
...   return sayspam
...
>>> @spamrun
... def useful(a, b):
...   print a**2 + b**2
...
>>> useful(3,4)
spam, spam, spam

最后,一个表现更良好的 decorator 可以在某些方面增强或修改未修饰函数的操作:
清单 7. 修改未修饰函数行为的 decorator

>>> def addspam(fn):
...   def new(*args):
...     print "spam, spam, spam"
...     return fn(*args)
...   return new
...
>>> @addspam
... def useful(a, b):
...   print a**2 + b**2
...
>>> useful(3,4)
spam, spam, spam
25

您可能会质疑,useful() 到底有多么有用?addspam() 真的是那样出色的增强 吗?但这种机制至少符合您通常能在有用的 decorator 中看到的那种模式。

高级抽象简介

根据我的经验,元类应用最多的场合就是在类实例化之后对类中的方法进行修改。decorator 目前并不允许您修改类实例化本身,但是它们可以修改依附于类的方法。这并不能让您在实例化过程中动态添加或删除方法或类属性,但是它让这些方法可以在运行时根据环境的条件来变更其行为。现在从技术上来说,decorator 是在运行 class 语句时应用的,对于顶级类来说,它更接近于 “编译时” 而非 “运行时”。但是安排 decorator 的运行时决策与创建类工厂一样简单。例如:
清单 8. 健壮但却深度嵌套的 decorator

def arg_sayer(what):
  def what_sayer(meth):
    def new(self, *args, **kws):
      print what
      return meth(self, *args, **kws)
    return new
  return what_sayer
Python 相关文章推荐
Python随机生成一个6位的验证码代码分享
Mar 24 Python
Python中struct模块对字节流/二进制流的操作教程
Jan 21 Python
python实时分析日志的一个小脚本分享
May 07 Python
使用python中的in ,not in来检查元素是不是在列表中的方法
Jul 06 Python
selenium+python 对输入框的输入处理方法
Oct 11 Python
python 串口读取+存储+输出处理实例
Dec 26 Python
在Python中利用pickle保存变量的实例
Dec 30 Python
python 实现 hive中类似 lateral view explode的功能示例
May 18 Python
python 写函数在一定条件下需要调用自身时的写法说明
Jun 01 Python
python图片验证码识别最新模块muggle_ocr的示例代码
Jul 03 Python
教你用Python写一个植物大战僵尸小游戏
Apr 25 Python
python 定义函数 返回值只取其中一个的实现
May 21 Python
在Python的setuptools框架下生成egg的教程
Apr 13 #Python
简单介绍Python中的RSS处理
Apr 13 #Python
Python2.x和3.x下maketrans与translate函数使用上的不同
Apr 13 #Python
使用Pyrex来扩展和加速Python程序的教程
Apr 13 #Python
在Python中使用itertools模块中的组合函数的教程
Apr 13 #Python
Python中用Spark模块的使用教程
Apr 13 #Python
简单理解Python中基于生成器的状态机
Apr 13 #Python
You might like
收音机的保养
2021/03/01 无线电
PHP OPCode缓存 APC详细介绍
2010/10/12 PHP
第六章 php目录与文件操作
2011/12/30 PHP
PHP间隔一段时间执行代码的方法
2014/12/02 PHP
php通过修改header强制图片下载的方法
2015/03/24 PHP
php构造方法中析构方法在继承中的表现
2016/04/12 PHP
JavaScript的类型简单说明
2010/09/03 Javascript
读jQuery之十 事件模块概述
2011/06/27 Javascript
JQuery中$.each 和$(selector).each()的区别详解
2015/03/13 Javascript
jquery+php实现滚动的数字特效
2015/11/29 Javascript
JavaScript必知必会(六) delete in instanceof
2016/06/08 Javascript
jQuery on()方法绑定动态元素的点击事件无响应的解决办法
2016/07/07 Javascript
jQuery实现表格隔行及滑动,点击时变色的方法【测试可用】
2016/08/20 Javascript
教你如何在Node.js中使用jQuery
2016/08/28 Javascript
AngularJs  unit-testing(单元测试)详解
2016/09/02 Javascript
常见的浏览器Hack技巧整理
2017/06/29 Javascript
在React中如何优雅的处理事件响应详解
2017/07/24 Javascript
vue基于Element构建自定义树的示例代码
2017/09/19 Javascript
Vue中跨域及打包部署到nginx跨域设置方法
2019/08/26 Javascript
es6函数之箭头函数用法实例详解
2020/04/25 Javascript
NodeJS配置CORS实现过程详解
2020/12/02 NodeJs
python3之微信文章爬虫实例讲解
2017/07/12 Python
Python实现发送与接收邮件的方法详解
2018/03/28 Python
使用anaconda的pip安装第三方python包的操作步骤
2018/06/11 Python
python射线法判断一个点在图形区域内外
2019/06/28 Python
Python 实现opencv所使用的图片格式与 base64 转换
2020/01/09 Python
python数字类型math库原理解析
2020/03/02 Python
Html5与App的通讯方式详解
2019/10/24 HTML / CSS
英国奢侈品网站:MatchesFashion
2016/12/16 全球购物
英国领先的葡萄酒专家:Majestic Wine
2017/05/30 全球购物
会计的岗位职责
2014/03/15 职场文书
2014年乡镇领导个人整改措施
2014/09/19 职场文书
四风对照检查材料范文
2014/09/27 职场文书
检讨书范文大全
2015/05/07 职场文书
我在伊朗长大观后感
2015/06/16 职场文书
Win11运行育碧游戏总是崩溃怎么办 win11玩育碧游戏出现性能崩溃的解决办法
2022/04/06 数码科技