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笔记(1) 关于我们应不应该继续学习python
Oct 24 Python
Python和GO语言实现的消息摘要算法示例
Mar 10 Python
Python 专题二 条件语句和循环语句的基础知识
Mar 19 Python
python 执行shell命令并将结果保存的实例
May 11 Python
浅谈Pandas:Series和DataFrame间的算术元素
Dec 22 Python
Python 正则表达式匹配字符串中的http链接方法
Dec 25 Python
python3实现字符串操作的实例代码
Apr 16 Python
Python 变量的创建过程详解
Sep 02 Python
将python文件打包exe独立运行程序方法详解
Feb 12 Python
python 工具 字符串转numpy浮点数组的实现
Mar 14 Python
Python filter过滤器原理及实例应用
Aug 18 Python
python3环境搭建过程(利用Anaconda+pycharm)完整版
Aug 19 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
php正则
2006/07/07 PHP
用PHP读注册表
2006/10/09 PHP
用PHP实现WEB动态网页静态
2006/10/09 PHP
APMServ使用说明
2006/10/23 PHP
PHP中设置一个严格30分钟过期Session面试题的4种答案
2014/07/30 PHP
php多个文件及图片上传实例详解
2014/11/10 PHP
如何利用PHP实现上传图片功能详解
2020/09/24 PHP
JavaScript利用构造函数和原型的方式模拟C#类的功能
2014/03/06 Javascript
js获取鼠标点击的位置实现思路及代码
2014/05/09 Javascript
jQuery函数map()和each()介绍及异同点分析
2014/11/08 Javascript
jQuery插件bxSlider实现响应式焦点图
2015/04/12 Javascript
jQuery插件zTree实现的基本树与节点获取操作示例
2017/03/08 Javascript
基于Vue实现tab栏切换内容不断实时刷新数据功能
2017/04/13 Javascript
使用jQuery实现动态添加小广告
2017/07/11 jQuery
将 vue 生成的 js 上传到七牛的实例
2017/07/28 Javascript
Vue-cli-webpack搭建斗鱼直播步骤详解
2017/11/17 Javascript
javascript面向对象程序设计实践常用知识点总结
2019/07/29 Javascript
Vue的编码技巧与规范使用详解
2019/08/28 Javascript
原生js拖拽功能制作滑动条实例代码
2021/02/05 Javascript
[36:19]2018DOTA2亚洲邀请赛 小组赛 A组加赛 Newbee vs LGD
2018/04/03 DOTA
[26:52]LGD vs EG 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
Python中zip()函数用法实例教程
2014/07/31 Python
Python中的getopt函数使用详解
2015/07/28 Python
Python字符串、元组、列表、字典互相转换的方法
2016/01/23 Python
python去除扩展名的实例讲解
2018/04/23 Python
matlab中二维插值函数interp2的使用详解
2020/04/22 Python
Python的Django框架实现数据库查询(不返回QuerySet的方法)
2020/05/19 Python
python实现爱奇艺登陆密码RSA加密的方法示例详解
2020/05/27 Python
婴儿鞋,独特的婴儿服装和配件:Zutano
2018/11/03 全球购物
网络维护中文求职信
2014/01/03 职场文书
小学作文评语大全
2014/04/21 职场文书
社区反邪教工作方案
2014/06/16 职场文书
小班上学期个人总结
2015/02/12 职场文书
城南旧事观后感
2015/06/11 职场文书
学校标语口号大全
2015/12/26 职场文书
Python面向对象之内置函数相关知识总结
2021/06/24 Python