最近打算重新开始记录自己的学习过程,于是就捡起被自己废弃了一年多的博客。这篇学习笔记主要是记录近来看的有关Python装饰器的东西。

0. 什么是装饰器?

本质上来说,装饰器其实就是一个特殊功能的函数,这个特殊的功能就是:装饰另一个函数。举一个最简单的例子来说:

 def identify(f):
print 'Decorator identify called.'
return f

这里identify其实是一个装饰器,这个装饰器对输入的参数f不进行任何修饰,然后返回这个参数。其中的打印语句是用来判断这个装饰器是什么时候被调用的。接下去,我们可以这样用这个装饰器:

 @identify
def foo():
print 'foo called.'

装饰器的语法是以@开头,然后跟着装饰器函数的名字,然后一些可选的装饰器函数的参数。紧跟着是被修饰的函数的定义。上述带装饰器的函数定义可以看成以下语法糖:

 def foo():
print 'foo called.'
foo = identify(foo)

写一个简单的类来对上述例子做一下测试:

 # Decorators
def identify(f):
print 'Decorator identify called.'
return f
class SimpleDecorator(object):
"""
This is my first decorator test class.
""" def __init__(self):
super(SimpleDecorator, self).__init__()
print 'SimpleDecorator init.' @identify
def foo(self):
print 'Foo called.' def test(self):
self.foo()
if __name__ == '__main__':
print '---- Test SimpleDecorator begin. ----'
simpleDecorator = SimpleDecorator()
simpleDecorator.test()
print '---- Test SimpleDecorator end. ----'

为了简单起见,我这里没有把identify装饰器写成类静态方法,因为类静态方法的定义也是利用装饰器来的。运行上述的测试代码,得到如下的输出:

可以看到装饰器函数比类实例的初始化还早,这说明在利用装饰器定义foo函数的时候装饰器函数已经被调用了。

1. 注册函数的装饰器。

接下去,我们来定义一个稍微复杂一点的装饰器,这个装饰器能把被装饰的函数注册到一个全局的字典里面。

 _functions = {}
def register1(class_name):
def _register(f):
"""
This is register1 doc.
"""
print 'Decorator register1 called.'
global _functions
name = class_name + '.' + f.__name__
_functions[name] = f
print 'Function %s is registed.' % name
def warpper(*args, **kwargs):
"""
This is warpper doc.
"""
f(*args, **kwargs)
return warpper
return _register

首先,这是一个带参数的装饰器。我们可以先看看怎么用这个装饰器,

@register1('RegisterDecorator')
def foo1():
"""
This is the foo1 doc.
"""
print 'Foo1 called.'

接下去,结合装饰器的定义来分析一下上述的代码。首先对于这句定义 @register1('RegisterDecorator')可以理解成@(register1('RegisterDecorator'))这样的优先级,也就是我们先传入参数'RegisterDecorator'来调用register1函数,可以看到register1函数其实是返回一个_register函数,在这里我更愿意把_register看成真正的装饰器。所以这句定义@register1('RegisterDecorator')就可以看成@_register,而这个_register其实是定义在带有参数'RegisterDecorator'信息的作用域内部。接下去的@_register就跟不带参数的装饰器一样了,所以在定义foo1函数的时候_register函数会被调用,在这个函数内部,foo1会被注册在一个全局的字典内部,然后返回一个跟foo1一样功能的函数。

2.functools装饰器工具

我们在来深入讨论一下上述装饰器,上述装饰器虽然达到我们注册函数的装饰作用,但是其实你会发现,这个被装饰的函数foo1已经不是原来的foo1,比如foo1.__doc__和foo1.__name__已经不是原来foo1时的"This is the foo1 doc"和"foo1"。这是怎么回事呢?因为装饰器其实只是一个语法糖,被装饰的foo1其实等于_register里面返回的wrapper函数,也就是foo1 = _register(foo1),而_register返回的正是wrapper函数,所以此时的foo1.__doc__和foo1.__name__应该是"This is wrapper doc."和"wrapper"。那么要如何避免上述的情况的呢,那就是利用装饰器工具functools。其实functools也是提供一些装饰器,这个装饰器可以保证你在定义装饰器时保留原来函数的__doc__和__name__属性。具体例子如下:

 import functools 

 _functions = {}
def register1(class_name):
def _register(f):
"""
This is register1 doc.
"""
print 'Decorator register1 called.'
global _functions
name = class_name + '.' + f.__name__
_functions[name] = f
print 'Function %s is registed.' % name
def warpper(*args, **kwargs):
"""
This is warpper doc.
"""
f(*args, **kwargs)
return warpper
return _register def register2(class_name):
def _register(f):
"""
This is register2 doc.
"""
print 'Decorator register2 called.'
global _functions
name = class_name + '.' + f.__name__
_functions[name] = f
print 'Function %s is registed.' % name
@functools.wraps(f)
def warpper(*args, **kwargs):
"""
This is warpper doc.
"""
f(*args, **kwargs)
return warpper
return _register class RegisterDecorator(object):
"""
This is register Decorator.
The decorator registed the function in golbal dict.
""" def __init__(self):
super(RegisterDecorator, self).__init__()
print 'RegisterDecorator init.' @register1('RegisterDecorator')
def foo1(self):
"""
This is the foo1 doc.
"""
print 'Foo1 called.' @register2('RegisterDecorator')
def foo2(self):
"""
This is the foo2 doc.
"""
print 'Foo2 called' def test(self):
self.foo1()
self.foo2()
pass if __name__ == '__main__':
print '---- Test RegisterDecorator begin. ----'
registerDecorator = RegisterDecorator()
registerDecorator.test()
print 'The doc of foo1 is: ', registerDecorator.foo1.__doc__
print 'The name of foo1 is: ', registerDecorator.foo1.__name__
print 'The doc of foo2 is: ', registerDecorator.foo2.__doc__
print 'The name of foo2 is: ', registerDecorator.foo2.__name__
print '---- Test RegisterDecorator end. ----'

register2和register1唯一不同的是,regisrer2返回的是被装饰器 @functools.wraps 装饰过的warpper函数。上述测试代码的输出为:

关于上述的测试代码托管在github上:https://github.com/fengzaihou/PythonLearning/tree/master/Decorators

Python 装饰器学习心得的更多相关文章

  1. Python 装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...

  2. (转载)Python装饰器学习

    转载出处:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方 ...

  3. Python装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 ? 1 2 3 4 5 6 7 8 # -*- ...

  4. python 装饰器学习(decorator)

    最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...

  5. Python装饰器学习(九步入门)

    这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 ? 1 2 3 4 5 6 7 8 # -*- coding:gbk -*- '''示 ...

  6. python装饰器学习详解-函数部分

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 最近阅读<流畅的python>看见其用函数写装饰器部分写的很好,想写一些自己的读书笔记. ...

  7. Python 装饰器学习以及实际使用场景实践

    前言 前几天在看Flask框架,对于非常神奇的@语法,不是非常的理解,回来补装饰器的功课.阅读很多的关于装饰器的文章,自己整理一下,适合自己的思路的方法和例子,与大家分享. app = Flask(_ ...

  8. python装饰器学习笔记

    定义:本质上就是个函数,(装饰器其他函数)就是为了给其他函数添加附加功能 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 #-*-coding:utf-8-*- 1 imp ...

  9. 【转】九步学习python装饰器

    本篇日志来自:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 纯转,只字未改.只是为了学习一下装饰器.其实现在也是没有太看明白 ...

随机推荐

  1. Pycharm 增加 run 控制台缓冲行数

    Yes, you can change the value of idea.cycle.buffer.size in bin/idea.properties under the PyCharm ins ...

  2. LaTeX快速入门-蔡炎龙

    蔡老师的这个文档只有26页,非常简短称得上是快速入门的文档了,TeX的基本入门这个文档还做不到,仅仅是一个简单的引子,让大家管中窥豹,先简单使用,然后才会更加深入进去. 文档早一个版本是用CJK排版的 ...

  3. MySQL 用户管理及权限管理

    MySQL 默认有个root用户,但是这个用户权限太大,一般只在管理数据库时候才用.如果在项目中要连接 MySQL 数据库,则建议新建一个权限较小的用户来连接. 在 MySQL 命令行模式下输入如下命 ...

  4. 【BZOJ】【2741】【FOTILE模拟赛】L

    可持久化Trie+分块 神题……Orz zyf & lyd 首先我们先将整个序列搞个前缀异或和,那么某一段的异或和,就变成了两个数的异或和,所以我们就将询问[某个区间中最大的区间异或和]改变成 ...

  5. 【转】一种新型的Web缓存欺骗攻击技术

    为了减少WEB响应时延并减小WEB服务器负担,现在WEB缓存技术已经用的非常普遍了,除了专门的CDN,负载均衡以及反向代理现在也会缓存一部分的网页内容.这里我要介绍一种WEB缓存欺骗攻击技术,这种攻击 ...

  6. Cesium随笔(1)部署自己的项目 【转】

    Cesium是国外开发的基于Webgl的跨浏览器的三维地球显示的Javascript开源库,只要浏览器支持Webgl,html5就可以运行.甚至部分手机浏览器都可以运行 . 首先,对Cesium进行本 ...

  7. 关于Java的一些NIO框架的一点想法

    闲着有点无聊想写点东西. 问题:生活中工作中,会有人问我javaNIO框架里面 Netty Mina  xSocket Grizzly 等等哪个比较好? 在这里写一下自己的感受,也算是总结一下吧 在我 ...

  8. svn报错can only be performed on a version resource [at this time].

    报错 can only be performed on a version resource [at this time].   有的文件能提交,有的文件不能提交 猜想:是不是因为缓存问题方法:tea ...

  9. Back Track 5 之 漏洞攻击 && 密码攻击 && Windows下渗透工具

    网络漏洞攻击工具 Metasploit 先msfupdate升级: 然后选择msfconsole: 接下来: set LHOST 本机IP地址 setLPORT setg PAYLOAD window ...

  10. (转)ngui3.5.7 版本Scroll View实现方法

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://xyo123.blog.51cto.com/6369437/1405861 现在网 ...