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代码来解图片迷宫的方法整理
Apr 02 Python
django 将model转换为字典的方法示例
Oct 16 Python
python实现简易学生信息管理系统
Apr 05 Python
Python实现快速排序的方法详解
Oct 25 Python
Python中如何将一个类方法变为多个方法
Dec 30 Python
关于Tensorflow使用CPU报错的解决方式
Feb 05 Python
python GUI库图形界面开发之PyQt5单选按钮控件QRadioButton详细使用方法与实例
Feb 28 Python
python pyqtgraph 保存图片到本地的实例
Mar 14 Python
Python搭建Keras CNN模型破解网站验证码的实现
Apr 07 Python
解决virtualenv -p python3 venv报错的问题
Feb 05 Python
pyx文件 生成pyd 文件用于 cython调用的实现
Mar 04 Python
Django路由层如何获取正确的url
Jul 15 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程序效率优化的一些策略小结
2010/07/17 PHP
PHP UTF8中文字符截断函数代码
2012/09/11 PHP
PHP中的str_repeat函数在JavaScript中的实现
2013/09/16 PHP
CentOS安装php v8js教程
2015/02/26 PHP
解决PHP里大量数据循环时内存耗尽的方法
2015/10/10 PHP
showModelessDialog()使用详解
2006/09/21 Javascript
一个基于jquery的文本框记数器
2012/09/19 Javascript
js单独获取一个checkbox看其是否被选中
2014/09/22 Javascript
node.js中的buffer.Buffer.isBuffer方法使用说明
2014/12/14 Javascript
javascript实现动态加载CSS
2015/01/26 Javascript
jQuery基础的工厂函数以及定时器的经典实例分析
2016/05/20 Javascript
js获取隐藏元素的宽高
2017/02/24 Javascript
webpack配置文件和常用配置项介绍
2017/04/28 Javascript
vue.js todolist实现代码
2017/10/29 Javascript
Webpack框架核心概念(知识点整理)
2017/12/22 Javascript
BootStrap table实现表格行拖拽效果
2018/12/01 Javascript
Vue安装浏览器开发工具的步骤详解
2019/05/12 Javascript
详解vue项目中实现图片裁剪功能
2019/06/07 Javascript
vue如何使用外部特殊字体的操作
2020/07/30 Javascript
详解JavaScript之Array.reduce源码解读
2020/11/01 Javascript
JavaScript实现简单动态表格
2020/12/02 Javascript
Python实现FTP文件传输的实例
2019/07/07 Python
python代码如何实现余弦相似性计算
2020/02/09 Python
python实现批量命名照片
2020/06/18 Python
CSS3毛玻璃效果(blur)有白边问题的解决方法
2016/11/15 HTML / CSS
英国最大的天然和有机产品在线零售商之一:Big Green Smile
2020/05/06 全球购物
应届医学毕业生求职信分享
2013/12/02 职场文书
《手指教学》反思
2014/02/14 职场文书
土木工程专业推荐信
2014/02/19 职场文书
房屋出售协议书
2014/04/10 职场文书
环保志愿者活动总结
2014/06/27 职场文书
幼儿园大班开学寄语
2014/08/02 职场文书
关于读书的演讲稿500字
2014/08/27 职场文书
2014年班组建设工作总结
2014/12/01 职场文书
中学教师个人总结
2015/02/10 职场文书
pygame面向对象的飞行小鸟实现(Flappy bird)
2021/04/01 Python