python,decorator

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


 

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

 

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

 

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

 

Python's functions are objectsapi

函数是一个对象

 

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世界里,函数也是一种对象。让咱们看下下面这一个例子:app

 

 

[python]   view plain copy print ?
  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!dom

好了,函数也是一个对象的问题,暂时先讨论到这里。函数还有另一个颇有意思的属性:咱们能够在函数里面定义一个函数!异步

 

 

[python]   view plain copy print ?
  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 referencesasync

函数引用

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:
这也就意味着,一个函数能够返回另一个函数 :-) 咱们来看下:

 

 

[python]   view plain copy print ?
  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:

还有更屌的,你既然能够返回一个函数,固然也能够接受一个函数做为参数

 

[python]   view plain copy print ?
  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:

手动实现一个装饰器:

 

[python]   view plain copy print ?
  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就能够了。

 

[python]   view plain copy print ?
  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语法表示以下:

 

[python]   view plain copy print ?
  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 其实就是下面这段代码的简单写法。

 

[python]   view plain copy print ?
  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:

固然,你也能够同时使用多个装饰器:

 

 

[python]   view plain copy print ?
  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的装饰器语法

 

[python]   view plain copy print ?
  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:

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

 

 

 

[python]   view plain copy print ?
  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

向被装饰的函数传递参数

 
[python]   view plain copy print ?
  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参数就能够了。
 
[python]   view plain copy print ?
  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就能够了:
 
[python]   view plain copy print ?
  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:
在搞定这个问题以前,我先给出一点提示:
 
[python]   view plain copy print ?
  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),也能够表示其余东西。
 
[python]   view plain copy print ?
  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:
不要惊讶,下面咱们更简单一点。
 
[python]   view plain copy print ?
  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:
再调整一下,更精简一点:
 
[python]   view plain copy print ?
  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?
好了,回到主题。若是咱们可以经过调用一个函数生成一个装饰器,咱们固然也可以向这个函数传递参数。
 
[python]   view plain copy print ?
  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:
好了,大功告成,一个接受参数的装饰器就搞定了。
 
[python]   view plain copy print ?
  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:
 
[python]   view plain copy print ?
  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:
下面是如何使用:
 
[python]   view plain copy print ?
  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 

最佳实践

一、They are new as of Python 2.4, so be sure that's what your code is running on.
二、Decorators slow down the function call. Keep that in mind.
三、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.
四、Decorators wrap functions, which can make them hard to debug.

一、装饰器是在Python 2.4以后才有的特性,因此请检查你的版本。
二、请记住:装饰器将会带来性能问题。
三、装饰是不可逆的。虽然已经有hacks设计出了可逆的装饰器,可是基本没人这么作。因此一旦一个函数被装饰过了,就没法还原了。
四、装饰器包装了函数,使得调试更加困难。
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也是一个装饰器 :-)
 
[python]   view plain copy print ?
  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.:
有一个不得不讨论的问题是:装饰器到底能用来干什么?看起来很酷很牛逼,可是能有个实实在在的例子就更完美了。实际状况不一样,用法也就不同。常见的用法能够用来扩展一个方法(这个方法是其余的库里面的,你没办法修改)也能够用来方便调试(你不想修改原来的方法,只是想暂时看一下调试信息,以后就删掉了)。你能够用同一段代码扩展各类方法,举个例子:
 
[python]   view plain copy print ?
  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:
上面的这些装饰器你能够用在任何地方,不须要改写现有的代码。
 
[python]   view plain copy print ?
  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用来实现异步调用。装饰器实在是博大精深。
相关文章
相关标签/搜索