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
- def shout(word="yes"):
- return word.capitalize()+"!"
-
- print shout()
-
-
-
-
-
-
-
- scream = shout
-
-
-
-
-
-
-
-
- print scream()
-
-
-
-
-
-
-
- del shout
- try:
- print shout()
- except NameError, e:
- print e
-
-
- print scream()
-
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
好了,函數也是一個對象的問題,暫時先討論到這裏。函數還有另一個頗有意思的屬性:咱們能夠在函數裏面定義一個函數!異步
- def talk():
-
-
-
- def whisper(word="yes"):
- return word.lower()+"..."
-
-
-
-
- print whisper()
-
-
-
-
-
- talk()
-
-
-
-
-
-
- try:
- print whisper()
- except NameError, e:
- print e
-
Functions referencesasync
函數引用
OK, still here? Now the fun part, you've seen that functions are objects and therefore:
好了,接下來就是見證奇蹟的時刻。咱們剛剛已經討論過了,函數也是一個對象,也就是說:
- can be assigned to a variable;
- can be defined in another function.
- 能夠賦值給一個變量
- 能夠在函數裏面定義
Well, that means that a function can return another function :-) Have a look:
這也就意味着,一個函數能夠返回另一個函數 :-) 咱們來看下:
- def getTalk(type="shout"):
-
-
-
- def shout(word="yes"):
- return word.capitalize()+"!"
-
- def whisper(word="yes") :
- return word.lower()+"...";
-
-
-
- if type == "shout":
-
-
-
- return shout
- else:
- return whisper
-
-
-
-
-
-
-
- talk = getTalk()
-
-
-
- print talk
-
-
-
-
- print talk()
-
-
-
-
- print getTalk("whisper")()
-
But wait, there is more. If you can return a function, then you can pass one as a parameter:
還有更屌的,你既然能夠返回一個函數,固然也能夠接受一個函數做爲參數
- def doSomethingBefore(func):
- print "I do something before then I call the function you gave me"
- print func()
-
- doSomethingBefore(scream)
-
-
-
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:
手動實現一個裝飾器:
-
-
- def my_shiny_new_decorator(a_function_to_decorate):
-
-
-
-
-
-
-
-
- def the_wrapper_around_the_original_function():
-
-
-
-
- print "Before the function runs"
-
-
-
- a_function_to_decorate()
-
-
-
-
- print "After the function runs"
-
-
-
-
-
-
-
-
-
- return the_wrapper_around_the_original_function
-
-
-
- def a_stand_alone_function():
- print "I am a stand alone function, don't you dare modify me"
-
- a_stand_alone_function()
-
-
-
-
-
-
-
-
-
-
- a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
- a_stand_alone_function_decorated()
-
-
-
-
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就能夠了。
- a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
- a_stand_alone_function()
-
-
-
-
-
-
-
Decorators demystified
Decorators 揭祕
The previous example, using the decorator syntax:
以前的那個例子,用decorator語法表示以下:
- @my_shiny_new_decorator
- def another_stand_alone_function():
- print "Leave me alone"
-
- another_stand_alone_function()
-
-
-
-
Yes, that's all, it's that simple. @decorator is just a shortcut to:
沒錯!就是那麼簡單!!@decorator 其實就是下面這段代碼的簡單寫法。
- 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:
固然,你也能夠同時使用多個裝飾器:
- def bread(func):
- def wrapper():
- print "</''''''\>"
- func()
- print "<\______/>"
- return wrapper
-
- def ingredients(func):
- def wrapper():
- print "#tomatoes#"
- func()
- print "~salad~"
- return wrapper
-
- def sandwich(food="--ham--"):
- print food
-
- sandwich()
-
- sandwich = bread(ingredients(sandwich))
- sandwich()
-
-
-
-
-
-
Using the Python decorator syntax:
使用Python的裝飾器語法
- @bread
- @ingredients
- def sandwich(food="--ham--"):
- print food
-
- sandwich()
-
-
-
-
-
-
The order you set the decorators MATTERS:
裝飾器的使用順序也是有影響的
- @ingredients
- @bread
- def strange_sandwich(food="--ham--"):
- print food
-
- strange_sandwich()
-
-
-
-
-
-
Passing arguments to the decorated function
向被裝飾的函數傳遞參數
-
-
-
-
- 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
-
-
-
-
-
-
-
- @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")
-
-
-
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參數就能夠了。
- def method_friendly_decorator(method_to_decorate):
- def wrapper(self, lie):
- lie = lie - 3
- 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)
-
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就能夠了:
- def a_decorator_passing_arbitrary_arguments(function_to_decorate):
-
- def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
- print "Do I have args?:"
- print args
- print kwargs
-
-
-
- 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()
-
-
-
-
-
-
- @a_decorator_passing_arbitrary_arguments
- def function_with_arguments(a, b, c):
- print a, b, c
-
- function_with_arguments(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!")
-
-
-
-
-
-
- class Mary(object):
-
- def __init__(self):
- self.age = 31
-
- @a_decorator_passing_arbitrary_arguments
- def sayYourAge(self, lie=-3):
- print "I am %s, what did you think ?" % (self.age + lie)
-
- m = Mary()
- m.sayYourAge()
-
-
-
-
-
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:
在搞定這個問題以前,我先給出一點提示:
-
-
- def my_decorator(func):
- print "I am a ordinary function"
- def wrapper():
- print "I am function returned by the decorator"
- func()
- return wrapper
-
-
-
-
- def lazy_function():
- print "zzzzzzzz"
-
- decorated_function = my_decorator(lazy_function)
-
-
-
-
-
-
- @my_decorator
- def lazy_function():
- print "zzzzzzzz"
-
-
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),也能夠表示其餘東西。
- def decorator_maker():
-
- print "I make decorators! I am executed only once: "+\
- "when you make me create a decorator."
-
- def my_decorator(func):
-
- print "I am a decorator! I am executed only when you decorate a function."
-
- def wrapped():
- print ("I am the wrapper around the decorated function. "
- "I am called when you call the decorated function. "
- "As the wrapper, I return the RESULT of the decorated function.")
- return func()
-
- print "As the decorator, I return the wrapped function."
-
- return wrapped
-
- print "As a decorator maker, I return a decorator"
- return my_decorator
-
-
-
-
- new_decorator = decorator_maker()
-
-
-
-
-
-
-
- def decorated_function():
- print "I am the decorated function."
-
- decorated_function = new_decorator(decorated_function)
-
-
-
-
-
-
- decorated_function()
-
-
-
-
No surprise here. Let's do EXACTLY the same thing, but skipping intermediate variables:
不要驚訝,下面咱們更簡單一點。
- def decorated_function():
- print "I am the decorated function."
- decorated_function = decorator_maker()(decorated_function)
-
-
-
-
-
-
-
- decorated_function()
-
-
-
-
Let's make it AGAIN, even shorter:
再調整一下,更精簡一點:
- @decorator_maker()
- def decorated_function():
- print "I am the decorated function."
-
-
-
-
-
-
-
- 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?
好了,回到主題。若是咱們可以經過調用一個函數生成一個裝飾器,咱們固然也可以向這個函數傳遞參數。
- 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):
-
-
-
- print "I am the decorator. Somehow you passed me arguments:", decorator_arg1, decorator_arg2
-
-
- 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")
-
-
-
-
-
-
-
-
-
Here it is, a decorator with arguments. Arguments can be set as variable:
好了,大功告成,一個接受參數的裝飾器就搞定了。
- c1 = "Penny"
- c2 = "Leslie"
-
- @decorator_maker_with_arguments("Leonard", c1)
- 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(c2, "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:
- def decorator_with_args(decorator_to_enhance):
-
-
-
-
-
-
-
-
-
- def decorator_maker(*args, **kwargs):
-
-
-
- def decorator_wrapper(func):
-
-
-
-
- return decorator_to_enhance(func, *args, **kwargs)
-
- return decorator_wrapper
-
- return decorator_maker
It can be used as follows:
下面是如何使用:
-
-
- @decorator_with_args
- def decorated_decorator(func, *args, **kwargs):
- def wrapper(function_arg1, function_arg2):
- print "Decorated with", args, kwargs
- return func(function_arg1, function_arg2)
- return wrapper
-
-
-
- @decorated_decorator(42, 404, 1024)
- def decorated_function(function_arg1, function_arg2):
- print "Hello", function_arg1, function_arg2
-
- decorated_function("Universe and", "everything")
-
-
-
-
-
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也是一個裝飾器 :-)
-
- def foo():
- print "foo"
-
- print foo.__name__
-
-
-
- def bar(func):
- def wrapper():
- print "bar"
- return func()
- return wrapper
-
- @bar
- def foo():
- print "foo"
-
- print foo.__name__
-
-
-
-
- import functools
-
- def bar(func):
-
-
- @functools.wraps(func)
- def wrapper():
- print "bar"
- return func()
- return wrapper
-
- @bar
- def foo():
- print "foo"
-
- print foo.__name__
-
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.:
有一個不得不討論的問題是:裝飾器到底能用來幹什麼?看起來很酷很牛逼,可是能有個實實在在的例子就更完美了。實際狀況不一樣,用法也就不同。常見的用法能夠用來擴展一個方法(這個方法是其餘的庫裏面的,你沒辦法修改)也能夠用來方便調試(你不想修改原來的方法,只是想暫時看一下調試信息,以後就刪掉了)。你能夠用同一段代碼擴展各類方法,舉個例子:
- def benchmark(func):
-
-
-
-
-
- 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):
-
-
-
-
-
-
- def wrapper(*args, **kwargs):
- res = func(*args, **kwargs)
- print func.__name__, args, kwargs
- return res
- return wrapper
-
-
- def counter(func):
-
-
-
-
- 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
- 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!")
-
-
-
-
-
-
-
-
-
-
Of course the good thing with decorators is that you can use them right away on almost anything without rewriting. DRY, I said:
上面的這些裝飾器你能夠用在任何地方,不須要改寫現有的代碼。
- @counter
- @benchmark
- @logging
- def get_random_futurama_quote():
- import httplib
- conn = httplib.HTTPConnection("slashdot.org:80")
- conn.request("HEAD", "/index.html")
- for key, value in conn.getresponse().getheaders():
- if key.startswith("x-b") or key.startswith("x-f"):
- return value
- return "No, I'm ... doesn't!"
-
- print get_random_futurama_quote()
- print get_random_futurama_quote()
-
-
-
-
-
-
-
-
-
-
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用來實現異步調用。裝飾器實在是博大精深。