python装饰器Decorators

来源:转载

http://blog.csdn.net/pipisorry/article/details/41902599


Introduction

装饰器Decorators是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。装饰器用于在不改变原函数代码的情况下修改已存在的函数。常见场景是增加一句调试,或者为已有的函数增加log监控。


装饰器为我们提供了一个增加已有函数或类的功能的有效方法。听起来是不是很像Java中的面向切面编程(Aspect-Oriented Programming)概念?两者都很简单,并且装饰器有着更为强大的功能。举个例子,假定你希望在一个函数的入口和退出点做一些特别的操作(比如一些安全、追踪以及锁定等操作)就可以使用装饰器。


装饰器是一个包装了另一个函数的特殊函数:主函数被调用,并且其返回值将会被传给装饰器,接下来装饰器将返回一个包装了主函数的替代函数,程序的其他部分看到的将是这个包装函数。def timethis(func):'''Decorator that reports the execution time.'''[email protected] countdown(n):while n > 0:n -= 1语法糖@标识了装饰器。


我们将用装饰器做一些更典型的操作:import timefrom functools import wrapsdef timethis(func):'''Decorator that reports the execution time.'''@wraps(func)def wrapper(*args, **kwargs):start = time.time()result = func(*args, **kwargs)end = time.time()print(func.__name__, end-start)return resultreturn [email protected] countdown(n):while n > 0:n -= 1countdown(100000)# ('countdown', 0.006999969482421875)当你写下如下代码时:@timethisdef countdown(n):意味着你分开执行了以下步骤:def countdown(n):...countdown = timethis(countdown)装饰器函数中的代码创建了一个新的函数(正如此例中的wrapper函数),它用 *args 和 **kwargs 接收任意的输入参数,并且在此函数内调用原函数并且返回其结果。你可以根据自己的需要放置任何额外的代码(例如本例中的计时操作),新创建的包装函数将作为结果返回并取代原函数。@decoratordef function():print("inside function")当编译器查看以上代码时,function()函数将会被编译,并且函数返回对象将会被传给装饰器代码,装饰器将会在做完相关操作之后用一个新的函数对象代替原函数。

Python装饰器要考虑装饰器本身的定义和被装饰器对象的定义。


对于无参数的装饰器,其装饰器函数的参数是要被装饰的函数对象名;


对于有参数的装饰器在调用时使用的是应用的参数,@timeStumpFunc_args(argv)的argv,已不再是要被装饰的函数对象名,所以必须在内部再定义一个函数getfunc()来接收要被装饰的函数对象。


时间装饰器的入门例子

装饰器的定义很是抽象,我们来看一个小例子。


def foo():


print 'in foo()'


foo()


看看执行这个函数用了多长时间,可以这样做:

import time


def foo():


start =time.clock()


print 'in foo()'


end =time.clock()


print 'used:', end -start

foo()



装饰器入门

考虑重新定义一个函数timeit,将foo的引用传递给他,然后在timeit中调用foo并进行计时,这样,我们就达到了不改动foo定义的目的

#!/usr/bin/env python
# coding=gbk
"""
__title__ = '带参数和不带参数的timeStump'
__author__ = 'pi'
__mtime__ = '2014.12.12'
"""
from time import ctimedef timeStumpFunc(func):
"""time stump decorator of func 不带参数的时间戳函数"""
def wrappedFunc(*nkw):
print("start_time %s" % ctime())
func(*nkw)
print("end_time %s" % ctime())
return wrappedFuncdef timeStumpFunc_args(args):
"""time stump decorator of func 不带参数的时间戳函数"""
print "timeStump for function %s" % args
def getFunc(func):
def wrappedFunc(*nkw):
print("start_time %s" % ctime())
func(*nkw)
print("end_time %s" % ctime())
return wrappedFunc
return [email protected]
# @timeStumpFunc_args('do_sth')
def do_sth(*nkw):
print "%s" % nkwif __name__ == '__main__':
do_sth('i you love')

皮皮blog

不同装饰器和被装饰对象的例子
一、函数式装饰器:装饰器本身是一个函数
1.装饰函数:被装饰对象是一个函数

[1]装饰器无参数:


a.被装饰对象无参数:

1 >>> def test(func):2 def _test():3print 'Call the function %s().'%func.func_name4return func()5 return _test6
7 >>> @test8 def say():return 'hello world'
9
10 >>> say() 11 Call the function say(). 12 'hello world'
13 >>>

b.被装饰对象有参数:

1 >>> def test(func):2 def _test(*args,**kw):3print 'Call the function %s().'%func.func_name4return func(*args,**kw)5 return _test6
7 >>> @test8 def left(Str,Len):9 #The parameters of _test can be '(Str,Len)' in this case.
10 return Str[:Len] 11
12 >>> left('hello world',5) 13 Call the function left(). 14 'hello'
15 >>>

[2]装饰器有参数:


a.被装饰对象无参数:

1 >>> def test(printResult=False):2 def _test(func):3def __test():4print 'Call the function %s().'%func.func_name5if printResult:6 print func()7else:8 return func()9return __test
10 return _test 11
12 >>> @test(True) 13 def say():return 'hello world'
14
15 >>> say() 16 Call the function say(). 17 hello world 18 >>> @test(False) 19 def say():return 'hello world'
20
21 >>> say() 22 Call the function say(). 23 'hello world'
24 >>> @test() 25 def say():return 'hello world'
26
27 >>> say() 28 Call the function say(). 29 'hello world'
30 >>> @test 31 def say():return 'hello world'
32
33 >>> say() 34
35 Traceback (most recent call last): 36 File "", line 1, in
37say() 38 TypeError: _test() takes exactly 1 argument (0 given) 39 >>>

Note:当装饰器有参数时,即使你启用装饰器的默认参数,不另外传递新值进去,也必须有一对括号,否则编译器会直接将func传递给test(),而不是传递给_test()

b.被装饰对象有参数:

1 >>> def test(printResult=False):2 def _test(func):3def __test(*args,**kw):4print 'Call the function %s().'%func.func_name5if printResult:6 print func(*args,**kw)7else:8 return func(*args,**kw)9return __test
10 return _test 11
12 >>> @test() 13 def left(Str,Len): 14 #The parameters of __test can be '(Str,Len)' in this case.
15 return Str[:Len] 16
17 >>> left('hello world',5) 18 Call the function left(). 19 'hello'
20 >>> @test(True) 21 def left(Str,Len): 22 #The parameters of __test can be '(Str,Len)' in this case.
23 return Str[:Len] 24
25 >>> left('hello world',5) 26 Call the function left(). 27 hello 28 >>>2.装饰类:被装饰的对象是一个类

[1]装饰器无参数:


a.被装饰对象无参数:

1 >>> def test(cls):2 def _test():3clsName=re.findall('(/w+)',repr(cls))[-1]4print 'Call %s.__init().'%clsName5return cls()6 return _test7
8 >>> @test9 class sy(object): 10 value=32
11
12
13 >>> s=sy() 14 Call sy.__init(). 15 >>> s 16 <__main__.sy object at 0x0000000002C3E390>
17 >>> s.value 18 32
19 >>>

b.被装饰对象有参数:

1 >>> def test(cls):2 def _test(*args,**kw):3clsName=re.findall('(/w+)',repr(cls))[-1]4print 'Call %s.__init().'%clsName5return cls(*args,**kw)6 return _test7
8 >>> @test9 class sy(object): 10 def __init__(self,value): 11 #The parameters of _test can be '(value)' in this case.
12self.value=value 13
14
15 >>> s=sy('hello world') 16 Call sy.__init(). 17 >>> s 18 <__main__.sy object at 0x0000000003AF7748>
19 >>> s.value 20 'hello world'
21 >>>

[2]装饰器有参数:


a.被装饰对象无参数:

1 >>> def test(printValue=True):2 def _test(cls):3def __test():4clsName=re.findall('(/w+)',repr(cls))[-1]5print 'Call %s.__init().'%clsName6obj=cls()7if printValue:8 print 'value = %r'%obj.value9return obj 10return __test
11 return _test 12
13 >>> @test() 14 class sy(object): 15 def __init__(self): 16self.value=32
17
18
19 >>> s=sy() 20 Call sy.__init(). 21 value = 32
22 >>> @test(False) 23 class sy(object): 24 def __init__(self): 25self.value=32
26
27
28 >>> s=sy() 29 Call sy.__init(). 30 >>>

b.被装饰对象有参数:

1 >>> def test(printValue=True):2 def _test(cls):3def __test(*args,**kw):4clsName=re.findall('(/w+)',repr(cls))[-1]5print 'Call %s.__init().'%clsName6obj=cls(*args,**kw)7if printValue:8 print 'value = %r'%obj.value9return obj 10return __test
11 return _test 12
13 >>> @test() 14 class sy(object): 15 def __init__(self,value): 16self.value=value 17
18
19 >>> s=sy('hello world') 20 Call sy.__init(). 21 value = 'hello world'
22 >>> @test(False) 23 class sy(object): 24 def __init__(self,value): 25self.value=value 26
27
28 >>> s=sy('hello world') 29 Call sy.__init(). 30 >>>
二、类式装饰器:装饰器本身是一个类

借用__init__()和__call__()来实现职能


1.装饰函数:被装饰对象是一个函数

[1]装饰器无参数:


a.被装饰对象无参数:

1 >>> class test(object):2 def __init__(self,func):3self._func=func4 def __call__(self):5return self._func()6
7
8 >>> @test9 def say(): 10 return 'hello world'
11
12 >>> say() 13 'hello world'
14 >>>

b.被装饰对象有参数:

1 >>> class test(object):2 def __init__(self,func):3self._func=func4 def __call__(self,*args,**kw):5return self._func(*args,**kw)6
7
8 >>> @test9 def left(Str,Len): 10 #The parameters of __call__ can be '(self,Str,Len)' in this case.
11 return Str[:Len] 12
13 >>> left('hello world',5) 14 'hello'
15 >>>

[2]装饰器有参数


a.被装饰对象无参数:

1 >>> class test(object):2 def __init__(self,beforeinfo='Call function'):3self.beforeInfo=beforeinfo4 def __call__(self,func):5def _call():6print self.beforeInfo7return func()8return _call9
10
11 >>> @test() 12 def say(): 13 return 'hello world'
14
15 >>> say() 16 Call function 17 'hello world'
18 >>>

或者:

1 >>> class test(object):2 def __init__(self,beforeinfo='Call function'):3self.beforeInfo=beforeinfo4 def __call__(self,func):5self._func=func6return self._call7 def _call(self):8print self.beforeInfo9return self._func() 10
11
12 >>> @test() 13 def say(): 14 return 'hello world'
15
16 >>> say() 17 Call function 18 'hello world'
19 >>>

b.被装饰对象有参数:

1 >>> class test(object):2 def __init__(self,beforeinfo='Call function'):3self.beforeInfo=beforeinfo4 def __call__(self,func):5def _call(*args,**kw):6print self.beforeInfo7return func(*args,**kw)8return _call9
10
11 >>> @test() 12 def left(Str,Len): 13 #The parameters of _call can be '(Str,Len)' in this case.
14 return Str[:Len] 15
16 >>> left('hello world',5) 17 Call function 18 'hello'
19 >>>

或者:

1 >>> class test(object):2 def __init__(self,beforeinfo='Call function'):3self.beforeInfo=beforeinfo4 def __call__(self,func):5self._func=func6return self._call7 def _call(self,*args,**kw):8print self.beforeInfo9return self._func(*args,**kw) 10
11
12 >>> @test() 13 def left(Str,Len): 14 #The parameters of _call can be '(self,Str,Len)' in this case.
15 return Str[:Len] 16
17 >>> left('hello world',5) 18 Call function 19 'hello'
20 >>>
2.装饰类:被装饰对象是一个类

[1]装饰器无参数:


a.被装饰对象无参数:

1 >>> class test(object):2 def __init__(self,cls):3self._cls=cls4 def __call__(self):5return self._cls()6
7
8 >>> @test9 class sy(object): 10 def __init__(self): 11self.value=32
12
13
14 >>> s=sy() 15 >>> s 16 <__main__.sy object at 0x0000000003AAFA20>
17 >>> s.value 18 32
19 >>>

b.被装饰对象有参数:

1 >>> class test(object):2 def __init__(self,cls):3self._cls=cls4 def __call__(self,*args,**kw):5return self._cls(*args,**kw)6
7
8 >>> @test9 class sy(object): 10 def __init__(self,value): 11#The parameters of __call__ can be '(self,value)' in this case.
12self.value=value 13
14
15 >>> s=sy('hello world') 16 >>> s 17 <__main__.sy object at 0x0000000003AAFA20>
18 >>> s.value 19 'hello world'
20 >>>

[2]装饰器有参数:


a.被装饰对象无参数:

1 >>> class test(object):2 def __init__(self,printValue=False):3self._printValue=printValue4 def __call__(self,cls):5def _call():6obj=cls()7if self._printValue:8 print 'value = %r'%obj.value9return obj 10return _call 11
12
13 >>> @test(True) 14 class sy(object): 15 def __init__(self): 16self.value=32
17
18
19 >>> s=sy() 20 value = 32
21 >>> s 22 <__main__.sy object at 0x0000000003AB50B8>
23 >>> s.value 24 32
25 >>>

b.被装饰对象有参数:

1 >>> class test(object):2 def __init__(self,printValue=False):3self._printValue=printValue4 def __call__(self,cls):5def _call(*args,**kw):6obj=cls(*args,**kw)7if self._printValue:8 print 'value = %r'%obj.value9return obj 10return _call 11
12
13 >>> @test(True) 14 class sy(object): 15 def __init__(self,value): 16#The parameters of _call can be '(value)' in this case.
17self.value=value 18
19
20 >>> s=sy('hello world') 21 value = 'hello world'
22 >>> s 23 <__main__.sy object at 0x0000000003AB5588>
24 >>> s.value 25 'hello world'
26 >>>

Note:


1. @decorator后面不带括号时(也即装饰器无参数时),效果就相当于先定义func或cls,而后执行赋值操作func=decorator(func)或cls=decorator(cls);


2. @decorator后面带括号时(也即装饰器有参数时),效果就相当于先定义func或cls,而后执行赋值操作 func=decorator(decoratorArgs)(func)或cls=decorator(decoratorArgs)(cls);


3. 如上将func或cls重新赋值后,此时的func或cls也不再是原来定义时的func或cls,而是一个可执行体,你只需要传入参数就可调用,func(args)=>返回值或者输出,cls(args)=>object of cls;


4. 最后通过赋值返回的执行体是多样的,可以是闭包,也可以是外部函数;当被装饰的是一个类时,还可以是类内部方法,函数;


5. 另外要想真正了解装饰器,一定要了解func.func_code.co_varnames,func.func_defaults,func.func_argcount,通过它们你可以以func的定义之外,还原func的参数列表,详见Python多重装饰器中的最后一个例子中的ArgsType;另外关键字参数是因为调用而出现的,而不是因为func的定义,func的定义中的用等号连接的只是有默认值的参数,它们并不一定会成为关键字参数,因为你仍然可以按照位置来传递它们。


皮皮blog

内置装饰器

内置的装饰器有三个,分别是staticmethod、classmethod和property,作用分别是把类中定义的实例方法变成静态方法、类方法和类属性。由于模块里可以定义函数,所以静态方法和类方法的用处并不是太多,除非你想要完全的面向对象编程。而属性也不是不可或缺的,Java没有属性也一样活得很滋润。从我个人的Python经验来看,我没有使用过property,使用staticmethod和classmethod的频率也非常低。




class Rabbit(object):



def __init__(self, name):


self._name=name





@staticmethod


def newRabbit(name):


return Rabbit(name)



@classmethod


def newRabbit2(cls):


return Rabbit('')



@property


def name(self):


return self._name




这里定义的属性是一个只读属性,如果需要可写,则需要再定义一个setter:



@name.setter


def name(self, name):


self._name=name


functools模块

functools模块提供了两个装饰器。[Python装饰器与面向切面编程]


wraps(wrapped[, assigned][, updated]): 这是一个很有用的装饰器。看过前一篇反射的朋友应该知道,函数是有几个特殊属性比如函数名,在被装饰后,上例中的函数名foo会变成包装函数的名字wrapper,如果你希望使用反射,可能会导致意外的结果。这个装饰器可以解决这个问题,它能将装饰过的函数的特殊属性保留。

total_ordering(cls): 这个装饰器在特定的场合有一定用处,但是它是在Python 2.7后新增的。它的作用是为实现了至少__lt__、__le__、__gt__、__ge__其中一个的类加上其他的比较方法,这是一个类装饰器。


皮皮blog

示例
在for循环中只运行其中的某个函数一次def run_once(f):def wrapper(*args, **kwargs):if not wrapper.has_run:wrapper.has_run = Truereturn f(*args, **kwargs)
wrapper.has_run = Falseprint('***')
return wrapperdef run_once1(f):def wrapper(*args, **kwargs):nonlocal has_run
if not has_run:has_run = Truereturn f(*args, **kwargs)
has_run = Falseprint('***')
return [email protected]_once1 def my_function(foo, bar):print(foo + bar)
return foo + bar for i in range(100):my_function(1, 2)[
Efficient way of having a function only execute once in a loop]

from:http://blog.csdn.net/pipisorry/article/details/41902599


ref:Python装饰器:简单装饰,带参数装饰与类装饰器


http://outofmemory.cn/code-snippet/1107/python-achieve-carry-parameter-decorator


Python decorators: metaprogramming with style


利用装饰器给python的函数加上类型限制


[神坑·Python 装饰类无限递归]


分享给朋友:
您可能感兴趣的文章:
随机阅读: