python decorator心得体会
来源:互联网 发布:cydia网络错误 编辑:程序博客网 时间:2024/05/08 18:39
python decorator心得体会
- 前言
- 用途
- 给方法添加新的功能
- 给类增加或者删除方法
- 参数化的decorator
- 更改方法的默认调用行为
- 2和3的整合
- 其实1和4可以归为一类特性,都是对现有方法的增强。
前言
此小短文来源于qq群一位朋友的问题,问题如下:
下面这段代码的功能是什么?def log(func): def wrapper(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return wrapper
经过仔细分析,这个log函数接受func作为参数,然后返回一个内部函数wrapper,同时如果外部使用者继续使用返回的wrapper继续调用的话,实际上会通过print打印日志,然后调用最先传递的参数func。
一个简单的测试例子如下
def addAB(a, b): return a+bprint log(addAB)(1,21) 执行输出结果如下:call addAB(): 22
对于上面的例子,拆解成两个部分分析。第一部分:使用log(addAB),它将addAB作为参数传递到log中,也就是说func=addAB,然后会返回内部的wrapper函数。第二部分: log(addAB)(1,21)被转换为wrapper(1,21),wrapper使用的可变参数形式,可以接受任意参数,wrapper函数首先打印print,然后调用func(*args, **kw),也就是相当于调用addAB(1,21),这也就可以说明print log(addAB)(1,21)的打印结果输出。
通过log的包装,对函数调用首先打印日志,然后再回到原始的函数调用。通过这个很简洁的函数,不需要修改func的代码,就可以对func函数实现调用日志的功能。后来,问这个问题的朋友告诉我了一种更简洁的方案,不需要显式的使用log,例子如下
@log def addAB(a, b): return a+bprint addAB(1,21)执行结果输出与上面相同
这种奇特的语法立刻燃起了我的兴趣,@log到底执行了什么?于是开始查看相关文档,找了一些文档,知道这种语法叫做decorator,常用在staticmethod和classmethod中。
python文档如下:
- functools
- term-decorator
- staticmethod
- types
用途
给方法添加新的功能
这个前面已经介绍过了,请参见给方法添加新的功能。
前面的例子通过decorator的包装,可以给函数的调用添加日志。当然你可以针对自己的需求,增加自己想要的功能。在python cookbook中有通过 decorator 添加了诸如跟踪、日志记录、存储/缓存、线程锁定以及输出重定向之类的功能(本人未看过),但是根据上面的内容,应该可以看出是可以做到的吧。
例子1:输出增加html格式输出
def h1(func): def wrapper(*args, **kw): return '<h1>'+func(*args, **kw)+'</h1>' return wrapperdef li(func): def wrapper(*args, **kw): return '<li>'+func(*args, **kw)+'</li>' return wrapper @h1@lidef foo(): return 'hello world'print foo() 执行输出如下<h1><li>hello world</li></h1>
可以看出经过两个decorator方法,输出结果确实增加了html标准语法格式。
例子2:用作缓存操作
cacheDic={}def cache(func): def wrapper(*args, **kw): #print 'args=', args, ' kw=',kw if args in cacheDic: return cacheDic[args] else: cacheDic[args] = func(*args, **kw) return cacheDic[args] return wrapper#@cachedef fib(n): if n < 2: return 1 else: return fib(n-1) + fib(n-2)import timeits = '[fib(x) for x in range(20)]'s_cache='[cache(fib)(x) for x in range(20)]'print timeit.timeit(stmt=s, setup='from __main__ import fib', number=100)print timeit.timeit(stmt=s_cache, setup='from __main__ import fib, cache', number=100) 输出结果如下:0.5700328350070.00699996948242
可以看出使用了cache之后,时间减少了100倍。这个是使用的timeit的统计方法。由于如果使用了@cache的方式,自动给fib函数增加了cache功能。为了表示对比,这里使用了显式的cache方案,使用了cache(fib)(x)这样的调用方式,它等价于使用@cache。
给类增加,删除,修改方法
角度1:模块加载时修改
通过改写类的__new__方法,可给类增加方法,删除方法,修改方法。基本方向是覆盖__new__方法,修改__new__方法的class参数,例如给class参数赋值新的方法,即可以给类增加新的method,或者del掉class的某一个方法,即可以删除类的方法,或者直接将类的某一个方法用的新的方法代替,即可以修改方法。不过需要注意的是,这些修改都是在new方法调用的时候完成的,即创建类对象的时候完成的,并不需要创建任何实例变量,或者说实在模块加载期间完成。角度2:运行期修改
可以通过decorator装饰器,在创建完实例对象之后调用方法的时刻,使其调用另外的实现代码,同样可以实现修改方法的目的。
具体请看下面的一个例子:
def minus(self, x, y): return x-y;def multi(self, x, y): return x*y;def div(self, x, y): return x*1.0/y;def sayHello(self, *args, **kw): print 'hello', self.__class__.__name__def runSayHelloDecorator(func): def wrapper(self, *args, **kw): func(*args, **kw) print 'in runtime, i have teached you to say hello runtime' return wrapperdef change_pc_method(func): print 'change' if func.__name__ != '__new__': return func #do not change other name method #here goes other add or delete or change method def wrapper(cls, *args, **kw): #add cls.minus = minus cls.multi = multi cls.div = div #change cls.sayHello = sayHello #delete if hasattr(cls, 'to_delete'): del cls.to_delete return super(cls.__class__, cls).__new__(cls, *args, **kw) return wrapperclass PC(object): def add(self, x, y): return x+y; @change_pc_method def __new__(): pass def to_delete(): print 'this method to be deleted' def sayHello(): print 'i can not say Hello, you can help me?' @runSayHelloDecorator def runSayHello(): print 'in runtime, i can not say hello'pc = PC()print pc.add(3,2),print pc.minus(3,2),print pc.div(3,2)pc.sayHello()pc.runSayHello()try: pc.to_delete()except Exception, e: print epc2=PC() 程序输出结果如下:change5 1 1.5hello PCin runtime, i can not say helloin runtime, i have teached you to say hello runtime'PC' object has no attribute 'to_delete'
上面的例子修改通过change_pc_method这种decorator覆盖了类的new方法,在其中对cls类对象增加了minus,multi,div三个方法,同时将其默认的sayHello的实现进行了修改。还有,通过运行期修饰函数调用的方式修改了runSayHello的实现。
注意:以上有一个小小的缺陷,在覆盖new方法修改sayHello中,将原有的sayHello给抛弃了。暂时并没有想到什么好的方案来做,很多时候原有的实现通常是有用的,这需要一些改进。如果您知道,请告知我。
参数化的decorator
参数化为运行时的决策提供了栖息地。本人并没有深刻理解这一思想的妙处,仅摆放在这里,便于后记。
#arg_wrapper, func_wrapper, wrapper#以后命名统一如此命名:arg, func,wrapperdef arg_wrapper(word): def func_wrapper(func): def wrapper(self, *args, **kw): print self, word return func( *args, **kw) #func本身已经携带self参数 return wrapper return func_wrapperdef FooMake(word): class Foo(object): @arg_wrapper(word) def say(): pass return Foo()foo1 = FooMake('this')foo2 = FooMake('that')foo1.say()foo2.say()程序输出结果:<decorator_parameter.Foo object at 0x6ffffe60bd0> this<decorator_parameter.Foo object at 0x6ffffe74c50> that
可以看出:say方法经过word参数化后具有不同的行为。不过本人还未知晓其用途
更改方法的默认调用行为
使用最基本的函数功能改变操作,通过装饰器扩展已有函数的功能。
def func_square(func):def wrapper(arg): if hasattr(arg, '__getitem__'): return map(func, arg); return func(arg) return wrapper@func_squaredef square(x): return x*x;print square(10) print square([i for i in range(10)])程序输出结果:100[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
原始的square方法只能对单个元素求平方,通过扩展square的功能,还可以对列表的所有元素求平方。
2和3的整合
在可爱的python一文中,提出了2和3的整合的一些引子。出于练习的角度,想写一个自动化一些的工具,奈何没有激情了,先就这样吧。
文章核心思想借用来源:可爱的 Python: Decorator 简化元编程
- python decorator心得体会
- python decorator
- python decorator
- python decorator
- python decorator
- Python - Decorator
- Python decorator
- python decorator
- python decorator
- Python Decorator
- Python Decorator
- python decorator
- Python Decorator
- python decorator
- Python - decorator
- Python Decorator
- Python - Decorator
- python decorator
- 如何用EA生成代码并将生成的代码放在VS搭建好的框架中
- 图片滑动组件iSlider
- Sync Object in OpenGL (about GPU Fence)
- 在Windows 7上搭建 Ruby环境
- Android 仿Morning routine 窗帘打开界面
- python decorator心得体会
- Java泛型
- 116 栢谷 鄭崑壽
- 平行四边形的效果实现
- EF6.0 学习(一)
- hihoCoder:#1039 字符消除
- 关机充电如何实现短按pwrkey灭屏
- 欢迎使用CSDN-markdown编辑器
- unix 的 bash shell 脚本