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世界里,函数也是一种对象。让我们看下下面这一个例子:
- 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!
好了,函数也是一个对象的问题,暂时先讨论到这里。函数还有另外一个很有意思的属性:我们可以在函数里面定义一个函数!
- def talk():
-
-
-
- def whisper(word="yes"):
- return word.lower()+"..."
-
-
-
-
- print whisper()
-
-
-
-
-
- talk()
-
-
-
-
-
-
- try:
- print whisper()
- except NameError, e:
- print e
-
Functions references
函数引用
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
最佳实践
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也是一个装饰器 :-)
-
- 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用来实现异步调用。装饰器实在是博大精深。