首页 > 代码库 > python装饰器Decorators

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.
‘‘‘
    pass


@timethis
def countdown(n):
    while n > 0:
        n -= 1
语法糖@标识了装饰器。

我们将用装饰器做一些更典型的操作:
import time
from functools import wraps
def 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 result
    return wrapper
 
@timethis
def countdown(n):
    while n > 0:
        n -= 1
 
countdown(100000)
 
# (‘countdown‘, 0.006999969482421875)
当你写下如下代码时:
@timethis
def countdown(n):
意味着你分开执行了以下步骤:


def countdown(n):
...
countdown = timethis(countdown)
装饰器函数中的代码创建了一个新的函数(正如此例中的wrapper函数),它用 *args 和 **kwargs 接收任意的输入参数,并且在此函数内调用原函数并且返回其结果。你可以根据自己的需要放置任何额外的代码(例如本例中的计时操作),新创建的包装函数将作为结果返回并取代原函数。


@decorator
def 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 ctime


def timeStumpFunc(func):
    """time stump decorator of func 不带参数的时间戳函数"""

    def wrappedFunc(*nkw):
        print("start_time %s" % ctime())
        func(*nkw)
        print("end_time %s" % ctime())

    return wrappedFunc


def 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 getFunc


@timeStumpFunc
# @timeStumpFunc_args(‘do_sth‘)
def do_sth(*nkw):
    print "%s" % nkw


if __name__ == ‘__main__‘:
    do_sth(‘i you love‘)


不同装饰器和被装饰对象的例子

一、函数式装饰器:装饰器本身是一个函数

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

[1]装饰器无参数:

a.被装饰对象无参数:

 1 >>> def test(func):
 2     def _test():
 3         print Call the function %s().%func.func_name
 4         return func()
 5     return _test
 6 
 7 >>> @test
 8 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):
 3         print Call the function %s().%func.func_name
 4         return func(*args,**kw)
 5     return _test
 6 
 7 >>> @test
 8 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):
 3         def __test():
 4             print Call the function %s().%func.func_name
 5             if printResult:
 6                 print func()
 7             else:
 8                 return func()
 9         return __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 "<pyshell#224>", line 1, in <module>
37     say()
38 TypeError: _test() takes exactly 1 argument (0 given)
39 >>> 
Note:当装饰器有参数时,即使你启用装饰器的默认参数,不另外传递新值进去,也必须有一对括号,否则编译器会直接将func传递给test(),而不是传递给_test()

b.被装饰对象有参数: 

 1 >>> def test(printResult=False):
 2     def _test(func):
 3         def __test(*args,**kw):
 4             print Call the function %s().%func.func_name
 5             if printResult:
 6                 print func(*args,**kw)
 7             else:
 8                 return func(*args,**kw)
 9         return __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():
 3         clsName=re.findall((\w+),repr(cls))[-1]
 4         print Call %s.__init().%clsName
 5         return cls()
 6     return _test
 7 
 8 >>> @test
 9 class sy(object):
10     value=http://www.mamicode.com/32"color:#008080;">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):
 3         clsName=re.findall((\w+),repr(cls))[-1]
 4         print Call %s.__init().%clsName
 5         return cls(*args,**kw)
 6     return _test
 7 
 8 >>> @test
 9 class sy(object):
10     def __init__(self,value):
11                 #The parameters of _test can be ‘(value)‘ in this case.
12         self.value=http://www.mamicode.com/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=http://www.mamicode.com/True):
 2     def _test(cls):
 3         def __test():
 4             clsName=re.findall((\w+),repr(cls))[-1]
 5             print Call %s.__init().%clsName
 6             obj=cls()
 7             if printValue:
 8                 print value = http://www.mamicode.com/%r%obj.value
 9             return obj
10         return __test
11     return _test
12 
13 >>> @test()
14 class sy(object):
15     def __init__(self):
16         self.value=http://www.mamicode.com/32"color:#008080;">17 
18         
19 >>> s=sy()
20 Call sy.__init().
21 value = http://www.mamicode.com/32"color:#008080;">22 >>> @test(False)
23 class sy(object):
24     def __init__(self):
25         self.value=http://www.mamicode.com/32"color:#008080;">26 
27         
28 >>> s=sy()
29 Call sy.__init().
30 >>> 

 

 b.被装饰对象有参数:

 1 >>> def test(printValue=http://www.mamicode.com/True):
 2     def _test(cls):
 3         def __test(*args,**kw):
 4             clsName=re.findall((\w+),repr(cls))[-1]
 5             print Call %s.__init().%clsName
 6             obj=cls(*args,**kw)
 7             if printValue:
 8                 print value = http://www.mamicode.com/%r%obj.value
 9             return obj
10         return __test
11     return _test
12 
13 >>> @test()
14 class sy(object):
15     def __init__(self,value):
16         self.value=http://www.mamicode.com/value
17 
18         
19 >>> s=sy(hello world)
20 Call sy.__init().
21 value = http://www.mamicode.com/hello world
22 >>> @test(False)
23 class sy(object):
24     def __init__(self,value):
25         self.value=http://www.mamicode.com/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):
 3         self._func=func
 4     def __call__(self):
 5         return self._func()
 6 
 7     
 8 >>> @test
 9 def say():
10     return hello world
11 
12 >>> say()
13 hello world
14 >>> 


b.被装饰对象有参数:

 1 >>> class test(object):
 2     def __init__(self,func):
 3         self._func=func
 4     def __call__(self,*args,**kw):
 5         return self._func(*args,**kw)
 6 
 7     
 8 >>> @test
 9 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):
 3         self.beforeInfo=beforeinfo
 4     def __call__(self,func):
 5         def _call():
 6             print self.beforeInfo
 7             return func()
 8         return _call
 9 
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):
 3         self.beforeInfo=beforeinfo
 4     def __call__(self,func):
 5         self._func=func
 6         return self._call
 7     def _call(self):
 8         print self.beforeInfo
 9         return 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):
 3         self.beforeInfo=beforeinfo
 4     def __call__(self,func):
 5         def _call(*args,**kw):
 6             print self.beforeInfo
 7             return func(*args,**kw)
 8         return _call
 9 
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):
 3         self.beforeInfo=beforeinfo
 4     def __call__(self,func):
 5         self._func=func
 6         return self._call
 7     def _call(self,*args,**kw):
 8         print self.beforeInfo
 9         return 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):
 3         self._cls=cls
 4     def __call__(self):
 5         return self._cls()
 6 
 7     
 8 >>> @test
 9 class sy(object):
10     def __init__(self):
11         self.value=http://www.mamicode.com/32"color:#008080;">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):
 3         self._cls=cls
 4     def __call__(self,*args,**kw):
 5         return self._cls(*args,**kw)
 6 
 7     
 8 >>> @test
 9 class sy(object):
10     def __init__(self,value):
11         #The parameters of __call__ can be ‘(self,value)‘ in this case.
12         self.value=http://www.mamicode.com/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=http://www.mamicode.com/False):
 3         self._printValue=http://www.mamicode.com/printValue
 4     def __call__(self,cls):
 5         def _call():
 6             obj=cls()
 7             if self._printValue:
 8                 print value = http://www.mamicode.com/%r%obj.value
 9             return obj
10         return _call
11 
12     
13 >>> @test(True)
14 class sy(object):
15     def __init__(self):
16         self.value=http://www.mamicode.com/32"color:#008080;">17 
18         
19 >>> s=sy()
20 value = http://www.mamicode.com/32"color:#008080;">21 >>> s
22 <__main__.sy object at 0x0000000003AB50B8>
23 >>> s.value
24 32
25 >>> 

  b.被装饰对象有参数:

 1 >>> class test(object):
 2     def __init__(self,printValue=http://www.mamicode.com/False):
 3         self._printValue=http://www.mamicode.com/printValue
 4     def __call__(self,cls):
 5         def _call(*args,**kw):
 6             obj=cls(*args,**kw)
 7             if self._printValue:
 8                 print value = http://www.mamicode.com/%r%obj.value
 9             return obj
10         return _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.
17         self.value=http://www.mamicode.com/value
18 
19         
20 >>> s=sy(hello world)
21 value = http://www.mamicode.com/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__其中一个的类加上其他的比较方法,这是一个类装饰器。

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 装饰类无限递归]


python装饰器Decorators