python装饰器Decorators

2021-01-19 10:21:10 浏览数 (1)

参考链接: Python装饰器

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@timethisdef 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 result    return wrapper @timethisdef countdown(n):    while n > 0:        n -= 1 countdown(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 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')

 皮皮blog

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

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

 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=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):

 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=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):

 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 = %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=32

17 

18         

19 >>> s=sy()

20 Call sy.__init().

21 value = 32

22 >>> @test(False)

23 class sy(object):

24     def __init__(self):

25         self.value=32

26 

27         

28 >>> s=sy()

29 Call sy.__init().

30 >>> 

  b.被装饰对象有参数:

   1 >>> def test(printValue=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 = %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=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):

25         self.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):

 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=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):

 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=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):

 3         self._printValue=printValue

 4     def __call__(self,cls):

 5         def _call():

 6             obj=cls()

 7             if self._printValue:

 8                 print 'value = %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=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):

 3         self._printValue=printValue

 4     def __call__(self,cls):

 5         def _call(*args,**kw):

 6             obj=cls(*args,**kw)

 7             if self._printValue:

 8                 print 'value = %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=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 = True

            return f(*args, **kwargs)

    wrapper.has_run = False

    print('***')

    return wrapper

def run_once1(f):

    def wrapper(*args, **kwargs):

        nonlocal has_run

        if not has_run:

            has_run = True

            return f(*args, **kwargs)

    has_run = False

    print('***')

    return wrapper

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

0 人点赞