python 学习笔记——python 装饰器(一)

来源:互联网 发布:好的幼儿教育软件app 编辑:程序博客网 时间:2024/05/17 23:35


装饰器的作用:常见的用法可以用来扩展一个方法(这个方法是其他的库里面的,你没办法修改)也可以用来方便调试(你不想修改原来的方法,只是想暂时看一下调试信息,之后就删掉了)

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


def benchmark(func):      """     A decorator that prints the time a function takes     to execute.     一个输出函数运行时间的装饰器     """      import time      def wrapper(*args, **kwargs):          t = time.clock()          res = func(*args, **kwargs)          print func.__name__, time.clock()-t          return res      return wrapper      def logging(func):      """     A decorator that logs the activity of the script.     一个输出日志信息的装饰器     (it actually just prints it, but it could be logging!)     虽然这里只是简单得只用了print函数,但是你可以用其他日志模块代替     """      def wrapper(*args, **kwargs):          res = func(*args, **kwargs)          print func.__name__, args, kwargs          return res      return wrapper      def counter(func):      """     A decorator that counts and prints the number of times a function has been executed     一个记录、打印函数调用次数的装饰器     """      def wrapper(*args, **kwargs):          wrapper.count = wrapper.count + 1          res = func(*args, **kwargs)          print "{0} has been used: {1}x".format(func.__name__, wrapper.count)          return res      wrapper.count = 0      return wrapper   @counter  @benchmark  @logging   #注意当有多个装饰器的时候执行的顺序是从内到外,装饰顺序为:@logging,@benchmark,@counter  先执行装饰器,最后执行被装饰函数体def reverse_string(string):      return str(reversed(string))    print reverse_string("Able was I ere I saw Elba")  print reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!")    #outputs:  #reverse_string ('Able was I ere I saw Elba',) {}      此时装饰器中的func.__name__ 是被装饰的函数名#wrapper 0.0                                          此时的装饰器的func.__name__ 是上层装饰器返回的wrapper#wrapper has been used: 1x                             此时的装饰器的func.__name__ 是上层装饰器返回的wrapper#ablE was I ere I saw elbA  #reverse_string ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura, maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag, a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash, a jar, sore hats, a peon, a canal: Panama!',) {}  #wrapper 0.0  #wrapper has been used: 2x  #!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)


对带参数的函数进行装饰


# It's not black magic, you just have to let the wrapper   # pass the argument:  # 这一点都不神奇,只要让包装器(wrapper)传递参数就可以了    def a_decorator_passing_arguments(function_to_decorate):      def a_wrapper_accepting_arguments(arg1, arg2):          print "I got args! Look:", arg1, arg2          function_to_decorate(arg1, arg2)      return a_wrapper_accepting_arguments    # Since when you are calling the function returned by the decorator, you are  # calling the wrapper, passing arguments to the wrapper will let it pass them to   # the decorated function  # 当你调用通过装饰器包装过后返回的函数时,   # 相当于调用包装器,并且将参数传递给包装器,由包装器将参数传递给原始函数。   @a_decorator_passing_arguments  def print_full_name(first_name, last_name):      print "My name is", first_name, last_name    print_full_name("Peter", "Venkman")  # outputs:  #I got args! Look: Peter Venkman  #My name is Peter Venkman  


对参数数量不确定的函数进行装饰

def a_decorator_passing_arbitrary_arguments(function_to_decorate):      # The wrapper accepts any arguments      def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):          print "Do I have args?:"          print args          print kwargs          # Then you unpack the arguments, here *args, **kwargs          # If you are not familiar with unpacking, check:          # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/          function_to_decorate(*args, **kwargs)      return a_wrapper_accepting_arbitrary_arguments   @a_decorator_passing_arbitrary_arguments  def function_with_no_argument():      print "Python is cool, no argument here."    function_with_no_argument()  #outputs  #Do I have args?:  #()  #{}  #Python is cool, no argument here.   @a_decorator_passing_arbitrary_arguments  def function_with_arguments(a, b, c):      print a, b, c    function_with_arguments(1,2,3)  #outputs  #Do I have args?:  #(1, 2, 3)  #{}  #1 2 3    @a_decorator_passing_arbitrary_arguments  def function_with_named_arguments(a, b, c, platypus="Why not ?"):      print "Do %s, %s and %s like platypus? %s" %\      (a, b, c, platypus)    function_with_named_arguments("Bill", "Linus", "Steve", platypus="Indeed!")  #outputs  #Do I have args ? :  #('Bill', 'Linus', 'Steve')  #{'platypus': 'Indeed!'}  #Do Bill, Linus and Steve like platypus? Indeed!    class Mary(object):        def __init__(self):          self.age = 31       @a_decorator_passing_arbitrary_arguments      def sayYourAge(self, lie=-3): # You can now add a default value          print "I am %s, what did you think ?" % (self.age + lie)    m = Mary()  m.sayYourAge()  #outputs  # Do I have args?:  #(<__main__.Mary object at 0xb7d303ac>,)  #{}  #I am 28, what did you think?  


装饰方法          #######  这里要注意的地方

What's great with Python is that methods and functions are really the same, except methods expect their first parameter to be a reference to the current object (self). It means you can build a decorator for methods the same way, just remember to take self in consideration:
在Python里面,方法(method)和函数(function)基本上是一样的,除了一点:方法的第一个参数必须是当前对象(self)的引用。也就是说你可以用同样的方法来装饰方法(method),只要记得处理self参数就可以了。 

def method_friendly_decorator(method_to_decorate):      def wrapper(self, lie):          lie = lie - 3 # very friendly, decrease age even more :-)          return method_to_decorate(self, lie)      return wrapper      class Lucy(object):        def __init__(self):          self.age = 32       @method_friendly_decorator      def sayYourAge(self, lie):          print "I am %s, what did you think?" % (self.age + lie)    l = Lucy()  l.sayYourAge(-3)  #outputs: I am 26, what did you think?  


让装饰器带参数


def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):        #  一层封装,带参数是给装饰器的参数    print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2        def my_decorator(func):                      #  二层封装,带的参数是被装饰的函数        # The ability to pass arguments here is a gift from closures.          # If you are not comfortable with closures, you can assume it's ok,          # or read: http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python          print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2            # Don't confuse decorator arguments and function arguments!          # 三层封装,对被封装的函数进行处理,此处的参数是被装饰函数的参数        # 二三层的封装就是装饰器没有参数的时候(也就是外层装饰器的参数是被装饰的函数的那种情况)        def wrapped(function_arg1, function_arg2) :                                 print ("I am the wrapper around the decorated function.\n"                    "I can access all the variables\n"                    "\t- from the decorator: {0} {1}\n"                    "\t- from the function call: {2} {3}\n"                    "Then I can pass them to the decorated function"                    .format(decorator_arg1, decorator_arg2,                            function_arg1, function_arg2))              return func(function_arg1, function_arg2)            return wrapped        return my_decorator   @decorator_maker_with_arguments("Leonard", "Sheldon")  def decorated_function_with_arguments(function_arg1, function_arg2):      print ("I am the decorated function and only knows about my arguments: {0}"             " {1}".format(function_arg1, function_arg2))    decorated_function_with_arguments("Rajesh", "Howard")  #outputs:  #I make decorators! And I accept arguments: Leonard Sheldon  #I am the decorator. Somehow you passed me arguments: Leonard Sheldon  #I am the wrapper around the decorated function.   #I can access all the variables   #   - from the decorator: Leonard Sheldon   #   - from the function call: Rajesh Howard   #Then I can pass them to the decorated function  #I am the decorated function and only knows about my arguments: Rajesh Howard  

注意:此时需要装饰器要进行三层封装,比没有带参数的装饰器多了外层的一层封装functools 的使用# For debugging, the stacktrace prints you the function __name__  def foo():      print "foo"    print foo.__name__  #outputs: foo    # With a decorator, it gets messy      def bar(func):      def wrapper():          print "bar"          return func()      return wrapper   @bar  def foo():      print "foo"    print foo.__name__  #outputs: wrapper    # "functools" can help for that    import functools    def bar(func):      # We say that "wrapper", is wrapping "func"      # and the magic begins      @functools.wraps(func)      def wrapper():          print "bar"          return func()      return wrapper   @bar  def foo():      print "foo"    print foo.__name__  #outputs: foo  
1、They are new as of Python 2.4, so be sure that's what your code is running on.
2、Decorators slow down the function call. Keep that in mind.
3、You can not un-decorate a function. There are hacks to create decorators that can be removed but nobody uses them. So once a function is decorated, it's done. For all the code.
4、Decorators wrap functions, which can make them hard to debug.

1、装饰器是在Python 2.4之后才有的特性,所以请检查你的版本。
2、请记住:装饰器将会带来性能问题。
3、装饰是不可逆的。虽然已经有hacks设计出了可逆的装饰器,但是基本没人这么做。所以一旦一个函数被装饰过了,就无法还原了。
4、装饰器包装了函数,使得调试更加困难。
Python 2.5 solves this last issue by providing the functools module including functools.wraps that copies the name, module and docstring of any wrapped function to it's wrapper. Fun fact, functools.wraps is a decorator :-)
Python 2.5 解决上面提到的第四个问题。Python 2.5 之后,包含了一个functools模块,这个模块提供一个functools.wraps方法,这个方法将被包装的函数的name, module 和 docstring 都复制到包装好的函数上去。显然,functools.wraps也是一个装饰器 


0 0
原创粉丝点击