相信积累的力量

python,decorator

http://blog.csdn.net/money_bear/article/details/11730729


 

在stackoverflow上面看到一个关于Python中装饰器问题的回复,瞬间觉得作者简直是神人啊。

 

原文地址:http://stackoverflow.com/questions/739654/how-can-i-make-a-chain-of-function-decorators-in-python

 

这么好的一段东西,实在是忍不住拿过来翻译一下,有删改:

 

Python's functions are objects

函数是一个对象

 

To understand decorators, you must first understand that functions are objects in Python. This has important consequences. Let's see why with a simple example :

要想理解装饰器,首先需要明白在Python世界里,函数也是一种对象。让我们看下下面这一个例子:

 

 

  1. def shout(word="yes"):  
  2.     return word.capitalize()+"!"  
  3.   
  4. print shout()  
  5. # outputs : 'Yes!'  
  6. # 输出 : 'Yes!'  
  7.   
  8. # As an object, you can assign the function to a variable like any  
  9. # other object   
  10. # 如果函数是个对象,那么它就可以赋值给变量  
  11.   
  12. scream = shout  
  13.   
  14. # Notice we don't use parentheses: we are not calling the function, we are  
  15. # putting the function "shout" into the variable "scream".   
  16. # It means you can then call "shout" from "scream":  
  17. # 注意这里并没有使用括号,也就是说没有调用这个函数  
  18. # 我们只是让变量 "scream" 等于函数 "shout"  
  19. # 意思就是你通过调用 "scream" 函数来调用 "shout" 函数:  
  20.   
  21. print scream()  
  22. # outputs : 'Yes!'  
  23.   
  24. # More than that, it means you can remove the old name 'shout', and  
  25. # the function will still be accessible from 'scream'  
  26. # 另外,这还表示你可以删除老的函数名 'shout'  
  27. # 这个函数依然存在,并且可以通过 'scream' 调用  
  28.   
  29. del shout  
  30. try:  
  31.     print shout()  
  32. except NameError, e:  
  33.     print e  
  34.     #outputs: "name 'shout' is not defined"  
  35.   
  36. print scream()  
  37. # outputs: 'Yes!'  

 

OK, keep that in mind, we are going back to it soon. Another interesting property of Python functions is they can be defined... inside another function!

好了,函数也是一个对象的问题,暂时先讨论到这里。函数还有另外一个很有意思的属性:我们可以在函数里面定义一个函数!

 

 

  1. def talk():  
  2.   
  3.     # You can define a function on the fly in "talk" ...  
  4.     # 你可以在 "talk" 函数运行的时候定义一个函数 ...  
  5.     def whisper(word="yes"):  
  6.         return word.lower()+"..."  
  7.   
  8.     # ... and use it right away!  
  9.     # ... 紧接着调用这个函数!  
  10.   
  11.     print whisper()  
  12.   
  13. # You call "talk", that defines "whisper" EVERY TIME you call it, then  
  14. # "whisper" is called in "talk".   
  15. # 每调用一次 "talk" 函数,就会定义一个 "whisper" 函数,   
  16. # 然后再调用这个刚刚定义的 "whisper" 函数 .   
  17. talk()  
  18. # outputs:   
  19. # "yes..."  
  20.   
  21. # But "whisper" DOES NOT EXIST outside "talk":  
  22. # 并且 "whisper" 函数在 "talk" 函数外部是不可见的:  
  23.   
  24. try:  
  25.     print whisper()  
  26. except NameError, e:  
  27.     print e  
  28.     #outputs : "name 'whisper' is not defined"*  

 

Functions references

函数引用

OK, still here? Now the fun part, you've seen that functions are objects and therefore:
好了,接下来就是见证奇迹的时刻。我们刚刚已经讨论过了,函数也是一个对象,也就是说:

 

  1. can be assigned to a variable;
  2. can be defined in another function.

 

 

  1. 可以赋值给一个变量
  2. 可以在函数里面定义

Well, that means that a function can return another function :-) Have a look:
这也就意味着,一个函数可以返回另外一个函数 :-) 我们来看下:

 

 

  1. def getTalk(type="shout"):  
  2.   
  3.     # We define functions on the fly  
  4.     # 定义一个函数  
  5.     def shout(word="yes"):  
  6.         return word.capitalize()+"!"  
  7.   
  8.     def whisper(word="yes") :  
  9.         return word.lower()+"...";  
  10.   
  11.     # Then we return one of them  
  12.     # 返回其中的而一个函数  
  13.     if type == "shout":  
  14.         # We don't use "()", we are not calling the function,  
  15.         # we are returning the function object  
  16.         # 再次注意:这里没有使用"()",我们并没有调用函数,而是将它作为返回值返回出去  
  17.         return shout    
  18.     else:  
  19.         return whisper  
  20.   
  21. # How do you use this strange beast?  
  22. # 刚刚这函数写得那么纠结,到底有什么用呢?  
  23.   
  24. # Get the function and assign it to a variable  
  25. # 调用 getTalk 函数,将返回的函数赋值给一个变量  
  26.   
  27. talk = getTalk()        
  28.   
  29. # You can see that "talk" is here a function object:  
  30. # 现在 "talk" 变成了一个函数对象  
  31. print talk  
  32. #outputs : <function shout at 0xb7ea817c>  
  33.   
  34. # The object is the one returned by the function:  
  35. # 看下调用这个函数会返回什么  
  36. print talk()  
  37. #outputs : Yes!  
  38.   
  39. # And you can even use it directly if you feel wild:  
  40. # 当然您也可以直接调用它:  
  41. print getTalk("whisper")()  
  42. #outputs : yes...  

 

But wait, there is more. If you can return a function, then you can pass one as a parameter:

还有更屌的,你既然可以返回一个函数,当然也可以接受一个函数作为参数

 

  1. def doSomethingBefore(func):   
  2.     print "I do something before then I call the function you gave me"  
  3.     print func()  
  4.   
  5. doSomethingBefore(scream)  
  6. #outputs:   
  7. #I do something before then I call the function you gave me  
  8. #Yes!  

 

Well, you just have everything needed to understand decorators. You see, decorators are wrappers which means that they let you execute code before and after the function they decorate without the need to modify the function itself.

好了,你已经搞明白了理解装饰器所需的所有基础知识。装饰器(decorator) 就是一个包装机(wrapper),让你能够在不修改原始函数的基础上,在执行函数的前后加入额外的代码

 


Handcrafted decorators

手工打造装饰器

How you would do it manually:

手动实现一个装饰器:

 

  1. # A decorator is a function that expects ANOTHER function as parameter  
  2. # 装饰器是一个函数,这个函数接收一个函数作为参数  
  3. def my_shiny_new_decorator(a_function_to_decorate):  
  4.   
  5.     # Inside, the decorator defines a function on the fly: the wrapper.  
  6.     # This function is going to be wrapped around the original function  
  7.     # so it can execute code before and after it.  
  8.     # 在装饰器(decorator)内部定义了一个函数即前面提到的包装机(wrapper)  
  9.     # 这个函数在原始函数的上下添加了一些代码  
  10.     # 这些代码在原始函数调用的前后执行.  
  11.   
  12.     def the_wrapper_around_the_original_function():  
  13.   
  14.         # Put here the code you want to be executed BEFORE the original   
  15.         # function is called、  
  16.         # 在原始函数前面添加代码,以便在原始函数调用之前执行  
  17.         print "Before the function runs"  
  18.   
  19.         # Call the function here (using parentheses)  
  20.         # 通过装饰器函数的参数调用原始函数  
  21.         a_function_to_decorate()  
  22.   
  23.         # Put here the code you want to be executed AFTER the original   
  24.         # function is called  
  25.         # 在原始函数的后面添加代码,以便在原始函数调用之后执行  
  26.         print "After the function runs"  
  27.   
  28.     # At this point, "a_function_to_decorate" HAS NEVER BEEN EXECUTED.  
  29.     # We return the wrapper function we have just created.  
  30.     # The wrapper contains the function and the code to execute before  
  31.     # and after. It's ready to use!  
  32.     # 函数执行到这里,"a_function_to_decorate" 到目前为止还没有执行  
  33.     # 我们返回刚刚创建的包装函数(wrapper function)  
  34.     # 这个包装函数(wrapper function)包含那些在原始函数执行前后需要被执行的代码  
  35.     # 这个返回的包装函数(wrapper function)可以被正常调用  
  36.     return the_wrapper_around_the_original_function  
  37.   
  38. # Now imagine you create a function you don't want to ever touch again.  
  39. # 加入你写了一个函数,你再也不想去碰它了  
  40. def a_stand_alone_function():  
  41.     print "I am a stand alone function, don't you dare modify me"  
  42.   
  43. a_stand_alone_function()   
  44. #outputs: I am a stand alone function, don't you dare modify me  
  45.   
  46. # Well, you can decorate it to extend its behavior.  
  47. # Just pass it to the decorator, it will wrap it dynamically in   
  48. # any code you want and return you a new function ready to be used:  
  49. # 为了给这个函数添加一些功能,你可以修饰(decorate)它。  
  50. # 只要将这个函数作为参数传递给一个装饰器函数,  
  51. # 那么这个装饰器函数就会动态的为这个待修饰的原始函数前后添加一些代码  
  52. # 并且返回一个新函数给你  
  53.   
  54. a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)  
  55. a_stand_alone_function_decorated()  
  56. #outputs:  
  57. #Before the function runs  
  58. #I am a stand alone function, don't you dare modify me  
  59. #After the function runs  

 

Now, you probably want that every time you call a_stand_alone_function, a_stand_alone_function_decorated is called instead. That's easy, just overwrite a_stand_alone_function with the function returned by my_shiny_new_decorator:

你可能觉得每次调用a_stand_alone_function函数的时候,都要用a_stand_alone_function_decorated来代替,这样一点都不优雅。解决这个问题其实很简单,只要把my_shiny_new_decorator函数返回的函数重新赋值给a_stand_alone_function就可以了。

 

  1. a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)  
  2. a_stand_alone_function()  
  3. #outputs:  
  4. #Before the function runs  
  5. #I am a stand alone function, don't you dare modify me  
  6. #After the function runs  
  7.   
  8. # And guess what? That's EXACTLY what decorators do!  
  9. # 你猜怎么着?Python里面的装饰器就是这么搞的  

 

Decorators demystified

Decorators 揭秘

The previous example, using the decorator syntax:

之前的那个例子,用decorator语法表示如下:

 

  1. @my_shiny_new_decorator  
  2. def another_stand_alone_function():  
  3.     print "Leave me alone"  
  4.   
  5. another_stand_alone_function()    
  6. #outputs:    
  7. #Before the function runs  
  8. #Leave me alone  
  9. #After the function runs  

 

Yes, that's all, it's that simple. @decorator is just a shortcut to:

没错!就是那么简单!!@decorator 其实就是下面这段代码的简单写法。

 

  1. another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)  

 

Decorators are just a pythonic variant of the decorator design pattern. There are several classic design patterns embedded in Python to ease development, like iterators.

装饰器其实就是装饰器模式的一种Python形式的方言。在Python里面还内置了一些其他的可以加速开发效率的典型的设计模式,比如说迭代器(iterators)
Of course, you can cumulate decorators:

当然,你也可以同时使用多个装饰器:

 

 

  1. def bread(func):  
  2.     def wrapper():  
  3.         print "</''''''\>"  
  4.         func()  
  5.         print "<\______/>"  
  6.     return wrapper  
  7.   
  8. def ingredients(func):  
  9.     def wrapper():  
  10.         print "#tomatoes#"  
  11.         func()  
  12.         print "~salad~"  
  13.     return wrapper  
  14.   
  15. def sandwich(food="--ham--"):  
  16.     print food  
  17.   
  18. sandwich()  
  19. #outputs: --ham--  
  20. sandwich = bread(ingredients(sandwich))  
  21. sandwich()  
  22. #outputs:  
  23. #</''''''\>  
  24. # #tomatoes#  
  25. # --ham--  
  26. # ~salad~  
  27. #<\______/>  

 

Using the Python decorator syntax:

使用Python的装饰器语法

 

  1. @bread  
  2. @ingredients  
  3. def sandwich(food="--ham--"):  
  4.     print food  
  5.   
  6. sandwich()  
  7. #outputs:  
  8. #</''''''\>  
  9. # #tomatoes#  
  10. # --ham--  
  11. # ~salad~  
  12. #<\______/>  

 

The order you set the decorators MATTERS:

装饰器的使用顺序也是有影响的

 

 

 

  1. @ingredients  
  2. @bread  
  3. def strange_sandwich(food="--ham--"):  
  4.     print food  
  5.   
  6. strange_sandwich()  
  7. #outputs:  
  8. ##tomatoes#  
  9. #</''''''\>  
  10. # --ham--  
  11. #<\______/>  
  12. # ~salad~  


Passing arguments to the decorated function

向被装饰的函数传递参数

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

Decorating methods

装饰方法

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参数就可以了。
 
  1. def method_friendly_decorator(method_to_decorate):  
  2.     def wrapper(self, lie):  
  3.         lie = lie - 3 # very friendly, decrease age even more :-)  
  4.         return method_to_decorate(self, lie)  
  5.     return wrapper  
  6.   
  7.   
  8. class Lucy(object):  
  9.   
  10.     def __init__(self):  
  11.         self.age = 32  
  12.  
  13.     @method_friendly_decorator  
  14.     def sayYourAge(self, lie):  
  15.         print "I am %s, what did you think?" % (self.age + lie)  
  16.   
  17. l = Lucy()  
  18. l.sayYourAge(-3)  
  19. #outputs: I am 26, what did you think?  

Of course, if you make a very general decorator and want to apply it to any function or method, no matter its arguments, then just use *args, **kwargs:
当然,如果你想设计一个通用的装饰器,不管函数还是方法都能用它来修饰,只要使用*args, **kwargs就可以了:
 
  1. def a_decorator_passing_arbitrary_arguments(function_to_decorate):  
  2.     # The wrapper accepts any arguments  
  3.     def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):  
  4.         print "Do I have args?:"  
  5.         print args  
  6.         print kwargs  
  7.         # Then you unpack the arguments, here *args, **kwargs  
  8.         # If you are not familiar with unpacking, check:  
  9.         # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/  
  10.         function_to_decorate(*args, **kwargs)  
  11.     return a_wrapper_accepting_arbitrary_arguments  
  12.  
  13. @a_decorator_passing_arbitrary_arguments  
  14. def function_with_no_argument():  
  15.     print "Python is cool, no argument here."  
  16.   
  17. function_with_no_argument()  
  18. #outputs  
  19. #Do I have args?:  
  20. #()  
  21. #{}  
  22. #Python is cool, no argument here.  
  23.  
  24. @a_decorator_passing_arbitrary_arguments  
  25. def function_with_arguments(a, b, c):  
  26.     print a, b, c  
  27.   
  28. function_with_arguments(1,2,3)  
  29. #outputs  
  30. #Do I have args?:  
  31. #(1, 2, 3)  
  32. #{}  
  33. #1 2 3   
  34.  
  35. @a_decorator_passing_arbitrary_arguments  
  36. def function_with_named_arguments(a, b, c, platypus="Why not ?"):  
  37.     print "Do %s, %s and %s like platypus? %s" %\  
  38.     (a, b, c, platypus)  
  39.   
  40. function_with_named_arguments("Bill""Linus""Steve", platypus="Indeed!")  
  41. #outputs  
  42. #Do I have args ? :  
  43. #('Bill', 'Linus', 'Steve')  
  44. #{'platypus': 'Indeed!'}  
  45. #Do Bill, Linus and Steve like platypus? Indeed!  
  46.   
  47. class Mary(object):  
  48.   
  49.     def __init__(self):  
  50.         self.age = 31  
  51.  
  52.     @a_decorator_passing_arbitrary_arguments  
  53.     def sayYourAge(self, lie=-3): # You can now add a default value  
  54.         print "I am %s, what did you think ?" % (self.age + lie)  
  55.   
  56. m = Mary()  
  57. m.sayYourAge()  
  58. #outputs  
  59. # Do I have args?:  
  60. #(<__main__.Mary object at 0xb7d303ac>,)  
  61. #{}  
  62. #I am 28, what did you think?  
 
Passing arguments to the decorator

向装饰器函数传递参数

Great, now what would you say about passing arguments to the decorator itself? Well this is a bit twisted because a decorator must accept a function as an argument and therefore, you cannot pass the decorated function arguments directly to the decorator.
好的,向装饰器传递参数是什么意思呢?装饰器必须只接受一个指向函数的参数,现在又要把参数直接传递给装饰器,这个问题有点纠结啊。
Before rushing to the solution, let's write a little reminder:
在搞定这个问题之前,我先给出一点提示:
 
  1. # Decorators are ORDINARY functions  
  2. # 装饰器是一个普通的函数  
  3. def my_decorator(func):  
  4.     print "I am a ordinary function"  
  5.     def wrapper():  
  6.         print "I am function returned by the decorator"  
  7.         func()  
  8.     return wrapper  
  9.   
  10. # Therefore, you can call it without any "@"  
  11. # 你可以直接调用它而不使用 "@"   
  12.   
  13. def lazy_function():  
  14.     print "zzzzzzzz"  
  15.   
  16. decorated_function = my_decorator(lazy_function)  
  17. #outputs: I am a ordinary function  
  18.   
  19. # It outputs "I am a ordinary function", because that's just what you do:  
  20. # calling a function. Nothing magic.  
  21. # 输出 "I am a ordinary function",没什么特别的  
  22.  
  23. @my_decorator  
  24. def lazy_function():  
  25.     print "zzzzzzzz"  
  26.   
  27. #outputs: I am a ordinary function  
 
It's exactly the same. "my_decorator" is called. So when you @my_decorator, you are telling Python to call the function 'labeled by the variable "my_decorator"'. It's important, because the label you give can point directly to the decorator... or not! Let's start to be evil!
以上两种调用方法效果都是一样的。当你使用@my_decorator这段代码时,你告诉Python解释器去调用my_decorator这个变量所代表的函数。注意,这就是关键!因为这个变量可以表示一个装饰器(decorator),也可以表示其他东西。
 
  1. def decorator_maker():  
  2.   
  3.     print "I make decorators! I am executed only once: "+\  
  4.           "when you make me create a decorator."  
  5.   
  6.     def my_decorator(func):  
  7.   
  8.         print "I am a decorator! I am executed only when you decorate a function."  
  9.   
  10.         def wrapped():  
  11.             print ("I am the wrapper around the decorated function. "  
  12.                   "I am called when you call the decorated function. "  
  13.                   "As the wrapper, I return the RESULT of the decorated function.")  
  14.             return func()  
  15.   
  16.         print "As the decorator, I return the wrapped function."  
  17.   
  18.         return wrapped  
  19.   
  20.     print "As a decorator maker, I return a decorator"  
  21.     return my_decorator  
  22.   
  23. # Let's create a decorator. It's just a new function after all.  
  24. # 现在创建一个装饰器,实际是一个新的函数。  
  25.   
  26. new_decorator = decorator_maker()         
  27. #outputs:  
  28. #I make decorators! I am executed only once: when you make me create a decorator.  
  29. #As a decorator maker, I return a decorator  
  30.   
  31. # Then we decorate the function  
  32. # 然后我们用它装饰一个函数  
  33.   
  34. def decorated_function():  
  35.     print "I am the decorated function."  
  36.   
  37. decorated_function = new_decorator(decorated_function)  
  38. #outputs:  
  39. #I am a decorator! I am executed only when you decorate a function.  
  40. #As the decorator, I return the wrapped function  
  41.   
  42. # Let's call the function:  
  43. # 最后调用这个函数  
  44. decorated_function()  
  45. #outputs:  
  46. #I am the wrapper around the decorated function. I am called when you call the decorated function.  
  47. #As the wrapper, I return the RESULT of the decorated function.  
  48. #I am the decorated function.  
 
No surprise here. Let's do EXACTLY the same thing, but skipping intermediate variables:
不要惊讶,下面我们更简单一点。
 
  1. def decorated_function():  
  2.     print "I am the decorated function."  
  3. decorated_function = decorator_maker()(decorated_function)  
  4. #outputs:  
  5. #I make decorators! I am executed only once: when you make me create a decorator.  
  6. #As a decorator maker, I return a decorator  
  7. #I am a decorator! I am executed only when you decorate a function.  
  8. #As the decorator, I return the wrapped function.  
  9.   
  10. # Finally:  
  11. decorated_function()      
  12. #outputs:  
  13. #I am the wrapper around the decorated function. I am called when you call the decorated function.  
  14. #As the wrapper, I return the RESULT of the decorated function.  
  15. #I am the decorated function.  
 
Let's make it AGAIN, even shorter:
再调整一下,更精简一点:
 
  1. @decorator_maker()  
  2. def decorated_function():  
  3.     print "I am the decorated function."  
  4. #outputs:  
  5. #I make decorators! I am executed only once: when you make me create a decorator.  
  6. #As a decorator maker, I return a decorator  
  7. #I am a decorator! I am executed only when you decorate a function.  
  8. #As the decorator, I return the wrapped function.  
  9.   
  10. #Eventually:   
  11. decorated_function()      
  12. #outputs:  
  13. #I am the wrapper around the decorated function. I am called when you call the decorated function.  
  14. #As the wrapper, I return the RESULT of the decorated function.  
  15. #I am the decorated function.  
 
Hey, did you see that? We used a function call with the "@" syntax :-)
好了,看见没有!我们在用@的时候调用了一个函数
So back to decorators with arguments. If we can use functions to generate the decorator on the fly, we can pass arguments to that function, right?
好了,回到主题。如果我们能够通过调用一个函数生成一个装饰器,我们当然也能够向这个函数传递参数。
 
  1. def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):  
  2.   
  3.     print "I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2  
  4.   
  5.     def my_decorator(func):  
  6.         # The ability to pass arguments here is a gift from closures.  
  7.         # If you are not comfortable with closures, you can assume it's ok,  
  8.         # or read: http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python  
  9.         print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2  
  10.   
  11.         # Don't confuse decorator arguments and function arguments!  
  12.         def wrapped(function_arg1, function_arg2) :  
  13.             print ("I am the wrapper around the decorated function.\n"  
  14.                   "I can access all the variables\n"  
  15.                   "\t- from the decorator: {0} {1}\n"  
  16.                   "\t- from the function call: {2} {3}\n"  
  17.                   "Then I can pass them to the decorated function"  
  18.                   .format(decorator_arg1, decorator_arg2,  
  19.                           function_arg1, function_arg2))  
  20.             return func(function_arg1, function_arg2)  
  21.   
  22.         return wrapped  
  23.   
  24.     return my_decorator  
  25.  
  26. @decorator_maker_with_arguments("Leonard""Sheldon")  
  27. def decorated_function_with_arguments(function_arg1, function_arg2):  
  28.     print ("I am the decorated function and only knows about my arguments: {0}"  
  29.            " {1}".format(function_arg1, function_arg2))  
  30.   
  31. decorated_function_with_arguments("Rajesh""Howard")  
  32. #outputs:  
  33. #I make decorators! And I accept arguments: Leonard Sheldon  
  34. #I am the decorator. Somehow you passed me arguments: Leonard Sheldon  
  35. #I am the wrapper around the decorated function.   
  36. #I can access all the variables   
  37. #   - from the decorator: Leonard Sheldon   
  38. #   - from the function call: Rajesh Howard   
  39. #Then I can pass them to the decorated function  
  40. #I am the decorated function and only knows about my arguments: Rajesh Howard  
 
Here it is, a decorator with arguments. Arguments can be set as variable:
好了,大功告成,一个接受参数的装饰器就搞定了。
 
  1. c1 = "Penny"  
  2. c2 = "Leslie"  
  3.  
  4. @decorator_maker_with_arguments("Leonard", c1)  
  5. def decorated_function_with_arguments(function_arg1, function_arg2):  
  6.     print ("I am the decorated function and only knows about my arguments:"  
  7.            " {0} {1}".format(function_arg1, function_arg2))  
  8.   
  9. decorated_function_with_arguments(c2, "Howard")  
  10. #outputs:  
  11. #I make decorators! And I accept arguments: Leonard Penny  
  12. #I am the decorator. Somehow you passed me arguments: Leonard Penny  
  13. #I am the wrapper around the decorated function.   
  14. #I can access all the variables   
  15. #   - from the decorator: Leonard Penny   
  16. #   - from the function call: Leslie Howard   
  17. #Then I can pass them to the decorated function  
  18. #I am the decorated function and only knows about my arguments: Leslie Howard  

As you can see, you can pass arguments to the decorator like any function using this trick. You can even use *args, **kwargs if you wish. But remember decorators are called only once. Just when Python imports the script. You can't dynamically set the arguments afterwards. When you do "import x", the function is already decorated, so you can't change anything.
因此,用这个技巧,你可以给任何函数的装饰器传递参数,甚至是*args, **kwargs这种形式的参数。但是需要记住的一点是,装饰器只会调用一次,在这之后你就无法动态的修改参数了。比如说,当你 "import x" 之后,这个函数就应该被装饰过了,无法再对他进行修改。
 
Let's practice: a decorator to decorate a decorator

练习:装饰装饰器的装饰器

OK, as a bonus, I'll give you a snippet to make any decorator accept generically any argument. After all, in order to accept arguments, we created our decorator using another function. We wrapped the decorator. Anything else we saw recently that wrapped function? Oh yes, decorators! Let's have some fun and write a decorator for the decorators:
 
  1. def decorator_with_args(decorator_to_enhance):  
  2.     """  
  3.     This function is supposed to be used as a decorator. 
  4.     It must decorate an other function, that is intended to be used as a decorator. 
  5.     Take a cup of coffee. 
  6.     It will allow any decorator to accept an arbitrary number of arguments, 
  7.     saving you the headache to remember how to do that every time. 
  8.     """  
  9.   
  10.     # We use the same trick we did to pass arguments  
  11.     def decorator_maker(*args, **kwargs):  
  12.   
  13.         # We create on the fly a decorator that accepts only a function  
  14.         # but keeps the passed arguments from the maker.  
  15.         def decorator_wrapper(func):  
  16.   
  17.             # We return the result of the original decorator, which, after all,   
  18.             # IS JUST AN ORDINARY FUNCTION (which returns a function).  
  19.             # Only pitfall: the decorator must have this specific signature or it won't work:  
  20.             return decorator_to_enhance(func, *args, **kwargs)  
  21.   
  22.         return decorator_wrapper  
  23.   
  24.     return decorator_maker  
 
It can be used as follows:
下面是如何使用:
 
  1. # You create the function you will use as a decorator. And stick a decorator on it :-)  
  2. # Don't forget, the signature is "decorator(func, *args, **kwargs)"  
  3. @decorator_with_args   
  4. def decorated_decorator(func, *args, **kwargs):   
  5.     def wrapper(function_arg1, function_arg2):  
  6.         print "Decorated with", args, kwargs  
  7.         return func(function_arg1, function_arg2)  
  8.     return wrapper  
  9.   
  10. # Then you decorate the functions you wish with your brand new decorated decorator.  
  11.  
  12. @decorated_decorator(424041024)  
  13. def decorated_function(function_arg1, function_arg2):  
  14.     print "Hello", function_arg1, function_arg2  
  15.   
  16. decorated_function("Universe and""everything")  
  17. #outputs:  
  18. #Decorated with (42, 404, 1024) {}  
  19. #Hello Universe and everything  
  20.   
  21. # Whoooot!  
 
I know, the last time you had this feeling, it was after listening a guy saying: "before understanding recursion, you must first understand recursion". But now, don't you feel good about mastering this?
如果有一个人跟你说:“在你理解递归之前,你首先需要先理解什么是递归。”你肯定觉得很纠结,我猜看了上面的代码,你现在也是同样的感觉。但是我洋洋洒洒写了那么多,你难道就一点感觉都没有吗?
 
Best practices while using decorators 

最佳实践

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也是一个装饰器 :-)
 
  1. # For debugging, the stacktrace prints you the function __name__  
  2. def foo():  
  3.     print "foo"  
  4.   
  5. print foo.__name__  
  6. #outputs: foo  
  7.   
  8. # With a decorator, it gets messy      
  9. def bar(func):  
  10.     def wrapper():  
  11.         print "bar"  
  12.         return func()  
  13.     return wrapper  
  14.  
  15. @bar  
  16. def foo():  
  17.     print "foo"  
  18.   
  19. print foo.__name__  
  20. #outputs: wrapper  
  21.   
  22. # "functools" can help for that  
  23.   
  24. import functools  
  25.   
  26. def bar(func):  
  27.     # We say that "wrapper", is wrapping "func"  
  28.     # and the magic begins  
  29.     @functools.wraps(func)  
  30.     def wrapper():  
  31.         print "bar"  
  32.         return func()  
  33.     return wrapper  
  34.  
  35. @bar  
  36. def foo():  
  37.     print "foo"  
  38.   
  39. print foo.__name__  
  40. #outputs: foo  
 
How can the decorators be useful?

装饰器到底有什么用

Now the big question: what can I use decorators for? Seem cool and powerful, but a practical example would be great. Well, there are 1000 possibilities. Classic uses are extending a function behavior from an external lib (you can't modify it) or for a debug purpose (you don't want to modify it because it's temporary). You can use them to extends several functions with the same code without rewriting it every time, for DRY's sake. E.g.:
有一个不得不讨论的问题是:装饰器到底能用来干什么?看起来很酷很牛逼,但是能有个实实在在的例子就更完美了。实际情况不同,用法也就不一样。常见的用法可以用来扩展一个方法(这个方法是其他的库里面的,你没办法修改)也可以用来方便调试(你不想修改原来的方法,只是想暂时看一下调试信息,之后就删掉了)。你可以用同一段代码扩展各种方法,举个例子:
 
  1. def benchmark(func):  
  2.     """ 
  3.     A decorator that prints the time a function takes 
  4.     to execute. 
  5.     一个输出函数运行时间的装饰器 
  6.     """  
  7.     import time  
  8.     def wrapper(*args, **kwargs):  
  9.         t = time.clock()  
  10.         res = func(*args, **kwargs)  
  11.         print func.__name__, time.clock()-t  
  12.         return res  
  13.     return wrapper  
  14.   
  15.   
  16. def logging(func):  
  17.     """ 
  18.     A decorator that logs the activity of the script. 
  19.     一个输出日志信息的装饰器 
  20.     (it actually just prints it, but it could be logging!) 
  21.     虽然这里只是简单得只用了print函数,但是你可以用其他日志模块代替 
  22.     """  
  23.     def wrapper(*args, **kwargs):  
  24.         res = func(*args, **kwargs)  
  25.         print func.__name__, args, kwargs  
  26.         return res  
  27.     return wrapper  
  28.   
  29.   
  30. def counter(func):  
  31.     """ 
  32.     A decorator that counts and prints the number of times a function has been executed 
  33.     一个记录、打印函数调用次数的装饰器 
  34.     """  
  35.     def wrapper(*args, **kwargs):  
  36.         wrapper.count = wrapper.count + 1  
  37.         res = func(*args, **kwargs)  
  38.         print "{0} has been used: {1}x".format(func.__name__, wrapper.count)  
  39.         return res  
  40.     wrapper.count = 0  
  41.     return wrapper  
  42.  
  43. @counter  
  44. @benchmark  
  45. @logging  
  46. def reverse_string(string):  
  47.     return str(reversed(string))  
  48.   
  49. print reverse_string("Able was I ere I saw Elba")  
  50. 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!")  
  51.   
  52. #outputs:  
  53. #reverse_string ('Able was I ere I saw Elba',) {}  
  54. #wrapper 0.0  
  55. #wrapper has been used: 1x   
  56. #ablE was I ere I saw elbA  
  57. #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!',) {}  
  58. #wrapper 0.0  
  59. #wrapper has been used: 2x  
  60. #!amanaP :lanac a ,noep a ,stah eros ,raj a ,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a ,)lemac a ro( niaga gab ananab a ,gat a ,nat a ,gab ananab a ,gag a ,inoracam ,elacrep ,epins ,spam ,arutaroloc a ,shajar ,soreh ,atsap ,eonac a ,nalp a ,nam A  
 
Of course the good thing with decorators is that you can use them right away on almost anything without rewriting. DRY, I said:
上面的这些装饰器你可以用在任何地方,不需要改写现有的代码。
 
  1. @counter  
  2. @benchmark  
  3. @logging  
  4. def get_random_futurama_quote():  
  5.     import httplib  
  6.     conn = httplib.HTTPConnection("slashdot.org:80")  
  7.     conn.request("HEAD""/index.html")  
  8.     for key, value in conn.getresponse().getheaders():  
  9.         if key.startswith("x-b"or key.startswith("x-f"):  
  10.             return value  
  11.     return "No, I'm ... doesn't!"  
  12.   
  13. print get_random_futurama_quote()  
  14. print get_random_futurama_quote()  
  15.   
  16. #outputs:  
  17. #get_random_futurama_quote () {}  
  18. #wrapper 0.02  
  19. #wrapper has been used: 1x  
  20. #The laws of science be a harsh mistress.  
  21. #get_random_futurama_quote () {}  
  22. #wrapper 0.01  
  23. #wrapper has been used: 2x  
  24. #Curse you, merciful Poseidon!  
 
Python itself provides several decorators: property, staticmethod, etc. Django use decorators to manage caching and view permissions. Twisted to fake inlining asynchronous functions calls. This really is a large playground.
Python自身也童工了一些装饰器:property, staticmethod。另外,Django使用装饰器管理缓存以及做权限控制。Twisted用来实现异步调用。装饰器实在是博大精深。
posted @ 2014-01-19 14:54  ThreeF  阅读(958)  评论(0编辑  收藏  举报

相信积累的力量