translator: http://www.jobbole.com/members/q3057027161/ reviewer: http://www.jobbole.com/members/hanxiaomax/

via: https://gist.github.com/Zearin/2f40b7b9cfc51132851a

NOTE: This is a question I found on StackOverflow which I’ve archived here, because the answer is so effing phenomenal.
注意: 这是一篇 StackOverflow 上的问题回答,因为这个回答很棒,所以我把它存档了


Q: How can I make a chain of function decorators in Python?

问: 怎样在 Python 中连续使用多个函数装饰器?


If you are not into long explanations, see Paolo Bergantino’s answer.
如果你不想看详细的解释,你可以看 Paolo Bergantino 的回答

Decorator Basics

装饰器基础

Python’s functions are objects

Python 的装饰器都是对象

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 中的函数都是 object 对象。 这非常重要。让我们通过一个例子来看看原因。

  1. def shout(word='yes'):
  2. return word.capitalize() + '!'
  3. print shout()
  4. # outputs : 'Yes!'
  5. # As an object, you can assign the function to a variable like any
  6. # other object
  7. # 作为一个 object 对象,你可以把一个函数分配给一个变量,就像是
  8. # 其他 object 对象一样
  9. scream = shout
  10. # Notice we don’t use parentheses: we are not calling the function, we are
  11. # putting the function `shout` into the variable `scream`.
  12. # It means you can then call `shout` from `scream`:
  13. # 请注意我们并没有使用括号:因此我们没有调用函数,我们只是把函数 `shout` 赋值给变量 `scream`
  14. # 这意味着我们可以通过 `scream` 调用 `shout` 函数
  15. print scream()
  16. # outputs : 'Yes!'
  17. # More than that, it means you can remove the old name `shout`, and
  18. # the function will still be accessible from `scream`
  19. # 除了这些,这还意味着你可以移除旧的函数名 `shout`,
  20. # 之后依然可以通过 `scream` 访问函数
  21. del shout
  22. try:
  23. print shout()
  24. except NameError as e:
  25. print e
  26. #outputs: "name 'shout' is not defined"
  27. print scream()
  28. # outputs: 'Yes!'

Okay! Keep this in mind. We’ll circle back to it shortly.
记住上面的内容,一会我们还会用得到。

Another interesting property of Python functions is they can be defined… inside another function!
Python 函数另一个有趣的性质在于它们可以。。。在另一个函数内部定义!

  1. def talk():
  2. # You can define a function on the fly in `talk` ...
  3. # 你可以在 `talk` 函数临时定义一个函数
  4. def whisper(word='yes'):
  5. return word.lower() + '...'
  6. # ... and use it right away!
  7. # ... 之后直接使用这个函数
  8. print whisper()
  9. # You call `talk`, that defines `whisper` EVERY TIME you call it, then
  10. # `whisper` is called in `talk`.
  11. # 你可以调用 `talk` 函数,每次调用这个函数都会定义 `whisper` 函数,并且
  12. # 在 `talk` 函数中调用 `whisper` 函数
  13. talk()
  14. # outputs:
  15. # "yes..."
  16. # But `whisper` DOES NOT EXIST outside `talk`:
  17. # 但是 `whisper` 函数在 `talk` 函数外部并不存在:
  18. try:
  19. print whisper()
  20. except NameError as e:
  21. print e
  22. #outputs : "name 'whisper' is not defined"*
  23. #Python's functions are objects

Functions references

函数引用

Okay, still here? Now the fun part…
现在是比较有趣的部分。。。

You’ve seen that functions are objects. Therefore, functions:
你已经知道了函数是 object 对象。此外,函数还:

  • can be assigned to a variable
  • can be defined in another function

  • 可以像变量一样赋值

  • 可以在另一个函数内部定义

That means that a function can return another function. Have a look! ☺
这表示 函数可以 return 另一个函数。看下面吧!☺

  1. def getTalk(kind='shout'):
  2. # We define functions on the fly
  3. # 我们临时定义一个函数
  4. def shout(word='yes'):
  5. return word.capitalize() + '!'
  6. def whisper(word='yes'):
  7. return word.lower() + '...'
  8. # Then we return one of them
  9. # 然后我们返回上面两个函数中的一个
  10. if kind == 'shout':
  11. # We don’t use '()'. We are not calling the function;
  12. # instead, we’re returning the function object
  13. # 我们并没有使用 '()' 。因此我们并没有调用函数;
  14. # 相反,我们返回了函数对象
  15. return shout
  16. else:
  17. return whisper
  18. # How do you use this strange beast?
  19. # 你该怎样使用这个奇怪的功能呢?
  20. # Get the function and assign it to a variable
  21. # 调用这个函数,然后把结果赋值给一个变量
  22. talk = getTalk()
  23. # You can see that `talk` is here a function object:
  24. # 你可以看到 `talk` 是一个函数对象:
  25. print talk
  26. #outputs : <function shout at 0xb7ea817c>
  27. # The object is the one returned by the function:
  28. # 这个对象是由一个函数返回的
  29. print talk()
  30. #outputs : Yes!
  31. # And you can even use it directly if you feel wild:
  32. # 如果你觉得奇怪的话,你甚至可以直接使用它
  33. print getTalk('whisper')()
  34. #outputs : yes...

But wait…there’s more!
但等等…还有一些内容!

If you can return a function, you can pass one as a parameter:
如果你可以 return 一个函数,那么你也可以把函数当作参数传递:

  1. def doSomethingBefore(func):
  2. print 'I do something before then I call the function you gave me'
  3. print func()
  4. doSomethingBefore(scream)
  5. #outputs:
  6. #I do something before then I call the function you gave me
  7. #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 modifying the function itself.
好,你已经掌握了装饰器所需的全部知识。正如你所见,装饰器是“包装器”,也就是说 它们允许你在它们装饰的函数的前面和后面运行其他代码 ,而不必修改函数本身。

Handcrafted decorators

动手制作装饰器

How you’d 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. # Inside, the decorator defines a function on the fly: the wrapper.
  5. # This function is going to be wrapped around the original function
  6. # so it can execute code before and after it.
  7. # 在装饰器内部,装饰器临时创建了一个函数: 包装器。
  8. # 这个函数把原来的函数包装起来
  9. # 因此它可以在原函数的前面和后面执行其他代码。
  10. def the_wrapper_around_the_original_function():
  11. # Put here the code you want to be executed BEFORE the original
  12. # function is called
  13. # 把你想在原函数被调用前执行的代码写在这里
  14. print 'Before the function runs'
  15. # Call the function here (using parentheses)
  16. # 在这里调用原函数(使用括号)
  17. a_function_to_decorate()
  18. # Put here the code you want to be executed AFTER the original
  19. # function is called
  20. # 把你想在原函数调用后执行的代码写在这里
  21. print 'After the function runs'
  22. # At this point, `a_function_to_decorate` HAS NEVER BEEN EXECUTED.
  23. # We return the wrapper function we have just created.
  24. # The wrapper contains the function and the code to execute before
  25. # and after. It’s ready to use!
  26. # 到目前为止,`a_function_to_decorate` 还从未执行过。
  27. # 我们返回刚刚创建的包装器
  28. # 包装器中包含了原函数和在原函数之前/之后执行的代码。现在已经可以使用了!
  29. return the_wrapper_around_the_original_function
  30. # Now imagine you create a function you don’t want to ever touch again.
  31. # 现在想象一下你创建了一个函数,你不想再改动它了。
  32. def a_stand_alone_function():
  33. print 'I am a stand alone function, don’t you dare modify me'
  34. a_stand_alone_function()
  35. #outputs: I am a stand alone function, don't you dare modify me
  36. # Well, you can decorate it to extend its behavior.
  37. # Just pass it to the decorator, it will wrap it dynamically in
  38. # any code you want and return you a new function ready to be used:
  39. # 好的,你可以装饰这个函数来扩展它的功能
  40. # 只需要把它传递给装饰器,之后就会动态地包装在你需要的任何代码中,然后返回一个满足你需求的新函数:
  41. a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
  42. a_stand_alone_function_decorated()
  43. #outputs:
  44. #Before the function runs
  45. #I am a stand alone function, don't you dare modify me
  46. #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. # And guess what? That’s EXACTLY what decorators do!
  8. # 你猜怎样着?这实际上就是装饰器的原理!

Decorators demystified

装饰器解密

The previous example, using the decorator syntax:
和前面相同的例子,但是使用了装饰器语法:

  1. @my_shiny_new_decorator
  2. def another_stand_alone_function():
  3. print 'Leave me alone'
  4. another_stand_alone_function()
  5. #outputs:
  6. #Before the function runs
  7. #Leave me alone
  8. #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).
装饰器只是一个 pythonic 的装饰器设计模式的变种。Python 中内置了许多种传统的设计模式来简化开发过程(例如迭代器)。

Of course, you can accumulate decorators:
当然,你可以叠加多个装饰器:

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

Using the Python decorator syntax:
使用 Python 的装饰器语法:

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

The order you set the decorators MATTERS:
你设置装饰器的顺序很重要:

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

Now: to answer the question…

现在:是时候回答问题了。。。

As a conclusion, you can easily see how to answer the question:
现在你很容易就知道怎样回答这个问题了:

  1. # The decorator to make it bold
  2. # 生成粗体(bold)的装饰器
  3. def makebold(fn):
  4. # The new function the decorator returns
  5. # 装饰器返回的新函数
  6. def wrapper():
  7. # Insertion of some code before and after
  8. # 在之前和之后插入其他代码
  9. return '<b>' + fn() + '</b>'
  10. return wrapper
  11. # The decorator to make it italic
  12. # 生成斜体的装饰器
  13. def makeitalic(fn):
  14. # The new function the decorator returns
  15. # 装饰器返回的新函数
  16. def wrapper():
  17. # Insertion of some code before and after
  18. # 在函数执行前后插入一些代码
  19. return '<i>' + fn() + '</i>'
  20. return wrapper
  21. @makebold
  22. @makeitalic
  23. def say():
  24. return 'hello'
  25. print say()
  26. #outputs: <b><i>hello</i></b>
  27. # This is the exact equivalent to
  28. # 和上面完全等价的形式
  29. def say():
  30. return 'hello'
  31. say = makebold(makeitalic(say))
  32. print say()
  33. #outputs: <b><i>hello</i></b>

You can now just leave happy, or burn your brain a little bit more and see advanced uses of decorators.
现在你该放下轻松的心态,好好看看装饰器的高级使用方法了。


Taking decorators to the next level

把装饰器传到下一层去

Passing arguments to the decorated function

把参数传递给被装饰的函数

  1. # It’s not black magic, you just have to let the wrapper
  2. # pass the argument:
  3. # 这并不是黑魔法,你只是让包装器传递参数而已
  4. def a_decorator_passing_arguments(function_to_decorate):
  5. def a_wrapper_accepting_arguments(arg1, arg2):
  6. print 'I got args! Look:', arg1, arg2
  7. function_to_decorate(arg1, arg2)
  8. return a_wrapper_accepting_arguments
  9. # Since when you are calling the function returned by the decorator, you are
  10. # calling the wrapper, passing arguments to the wrapper will let it pass them to
  11. # the decorated function
  12. # 因为当你调用装饰器返回的函数时,实际上你在调用包装器,把参数传递给包装器,这也就完成了把参数传递给装饰器函数
  13. @a_decorator_passing_arguments
  14. def print_full_name(first_name, last_name):
  15. print 'My name is', first_name, last_name
  16. print_full_name('Peter', 'Venkman')
  17. # outputs:
  18. #I got args! Look: Peter Venkman
  19. #My name is Peter Venkman

Decorating methods

装饰器方法

One nifty thing about Python is that methods and functions are really the same. The only difference is that methods expect that their first argument is a reference to the current object (self).
关于 Python 的一个优点就是方法和函数本质本质上是一样的。二者唯一的区别就是方法的第一个参数是对当前对象的引用 (self)。

That means you can build a decorator for methods the same way! Just remember to take self into consideration:
这意味着你可以按照同样的方式为方法创建装饰器!只要记得考虑 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. class Lucy(object):
  7. def __init__(self):
  8. self.age = 32
  9. @method_friendly_decorator
  10. def sayYourAge(self, lie):
  11. print 'I am {0}, what did you think?'.format(self.age + lie)
  12. l = Lucy()
  13. l.sayYourAge(-3)
  14. #outputs: I am 26, what did you think?

If you’re making general-purpose decorator—one you’ll apply 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. # 包装器接受任何参数
  4. def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
  5. print 'Do I have args?:'
  6. print args
  7. print kwargs
  8. # Then you unpack the arguments, here *args, **kwargs
  9. # If you are not familiar with unpacking, check:
  10. # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
  11. # 接下来解包参数,也就是这里的 *args, **kwargs
  12. # 如果你不熟悉解包,可以浏览这个:
  13. # http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
  14. function_to_decorate(*args, **kwargs)
  15. return a_wrapper_accepting_arbitrary_arguments
  16. @a_decorator_passing_arbitrary_arguments
  17. def function_with_no_argument():
  18. print 'Python is cool, no argument here.'
  19. function_with_no_argument()
  20. #outputs
  21. #Do I have args?:
  22. #()
  23. #{}
  24. #Python is cool, no argument here.
  25. @a_decorator_passing_arbitrary_arguments
  26. def function_with_arguments(a, b, c):
  27. print a, b, c
  28. function_with_arguments(1,2,3)
  29. #outputs
  30. #Do I have args?:
  31. #(1, 2, 3)
  32. #{}
  33. #1 2 3
  34. @a_decorator_passing_arbitrary_arguments
  35. def function_with_named_arguments(a, b, c, platypus='Why not ?'):
  36. print 'Do {0}, {1} and {2} like platypus? {3}'.format(
  37. a, b, c, platypus)
  38. function_with_named_arguments('Bill', 'Linus', 'Steve', platypus='Indeed!')
  39. #outputs
  40. #Do I have args ? :
  41. #('Bill', 'Linus', 'Steve')
  42. #{'platypus': 'Indeed!'}
  43. #Do Bill, Linus and Steve like platypus? Indeed!
  44. class Mary(object):
  45. def __init__(self):
  46. self.age = 31
  47. @a_decorator_passing_arbitrary_arguments
  48. def sayYourAge(self, lie=-3): # You can now add a default value 你可以在这里添加默认值
  49. print 'I am {0}, what did you think?'.format(self.age + lie)
  50. m = Mary()
  51. m.sayYourAge()
  52. #outputs
  53. # Do I have args?:
  54. #(<__main__.Mary object at 0xb7d303ac>,)
  55. #{}
  56. #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?
太棒了,现在你对于把参数传递给装饰器本身有什么看法呢?

This can get somewhat twisted, since a decorator must accept a function as an argument. Therefore, you cannot pass the decorated function’s 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 an ordinary function'
  5. def wrapper():
  6. print 'I am function returned by the decorator'
  7. func()
  8. return wrapper
  9. # Therefore, you can call it without any '@'
  10. # 因此你可以在没有任何 '@' 的情况下调用它
  11. def lazy_function():
  12. print 'zzzzzzzz'
  13. decorated_function = my_decorator(lazy_function)
  14. #outputs: I am an ordinary function
  15. # It outputs 'I am an ordinary function', because that’s just what you do:
  16. # calling a function. Nothing magic.
  17. # 上面的函数输出 'I am an ordinary function' ,因为这实际上就是我们直接调用函数的结果。没什么好奇怪的。
  18. @my_decorator
  19. def lazy_function():
  20. print 'zzzzzzzz'
  21. #outputs: I am an ordinary function

It’s exactly the same: my_decorator is called. So when you @my_decorator, you are telling Python to call the function labelled by the variable “my_decorator.
结果是一模一样的:my_decorator 被调用了。因此当你使用 @my_decorator 时,Python 会调用 my_decorator” 变量所代表的函数

This is important! The label you give can point directly to the decorator—or not.
这很重要!你提供的这个变量可以指向装饰器,也可以不指向

Let’s get evil. ☺
让我们增加点难度。 ☺

  1. def decorator_maker():
  2. print 'I make decorators! I am executed only once: '+\
  3. 'when you make me create a decorator.'
  4. def my_decorator(func):
  5. print 'I am a decorator! I am executed only when you decorate a function.'
  6. def wrapped():
  7. print ('I am the wrapper around the decorated function. '
  8. 'I am called when you call the decorated function. '
  9. 'As the wrapper, I return the RESULT of the decorated function.')
  10. return func()
  11. print 'As the decorator, I return the wrapped function.'
  12. return wrapped
  13. print 'As a decorator maker, I return a decorator'
  14. return my_decorator
  15. # Let’s create a decorator. It’s just a new function after all.
  16. # 让我们创建一个装饰器。本质上是一个新函数
  17. new_decorator = decorator_maker()
  18. #outputs:
  19. #I make decorators! I am executed only once: when you make me create a decorator.
  20. #As a decorator maker, I return a decorator
  21. # Then we decorate the function
  22. # 然后我们装饰下面这个函数
  23. def decorated_function():
  24. print 'I am the decorated function.'
  25. decorated_function = new_decorator(decorated_function)
  26. #outputs:
  27. #I am a decorator! I am executed only when you decorate a function.
  28. #As the decorator, I return the wrapped function
  29. # Let’s call the function:
  30. # 调用这个函数
  31. decorated_function()
  32. #outputs:
  33. #I am the wrapper around the decorated function. I am called when you call the decorated function.
  34. #As the wrapper, I return the RESULT of the decorated function.
  35. #I am the decorated function.

No surprise here.
没什么意料之外的事情发生。

Let’s do EXACTLY the same thing, but skip all the pesky 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. # Finally:
  10. # 最后:
  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 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. #Eventually:
  10. #最后:
  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. print 'I make decorators! And I accept arguments:', decorator_arg1, decorator_arg2
  3. def my_decorator(func):
  4. # The ability to pass arguments here is a gift from closures.
  5. # If you are not comfortable with closures, you can assume it’s ok,
  6. # or read: http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
  7. # 传递参数的能力来自于闭包
  8. # 如果你不了解闭包,那也没关系,
  9. # 或者你也可以阅读 http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
  10. print 'I am the decorator. Somehow you passed me arguments:', decorator_arg1, decorator_arg2
  11. # Don’t confuse decorator arguments and function arguments!
  12. # 不要混淆装饰器参数和函数参数!
  13. def wrapped(function_arg1, function_arg2):
  14. print ('I am the wrapper around the decorated function.\n'
  15. 'I can access all the variables\n'
  16. '\t- from the decorator: {0} {1}\n'
  17. '\t- from the function call: {2} {3}\n'
  18. 'Then I can pass them to the decorated function'
  19. .format(decorator_arg1, decorator_arg2,
  20. function_arg1, function_arg2))
  21. return func(function_arg1, function_arg2)
  22. return wrapped
  23. return my_decorator
  24. @decorator_maker_with_arguments('Leonard', 'Sheldon')
  25. def decorated_function_with_arguments(function_arg1, function_arg2):
  26. print ('I am the decorated function and only knows about my arguments: {0}'
  27. ' {1}'.format(function_arg1, function_arg2))
  28. decorated_function_with_arguments('Rajesh', 'Howard')
  29. #outputs:
  30. #I make decorators! And I accept arguments: Leonard Sheldon
  31. #I am the decorator. Somehow you passed me arguments: Leonard Sheldon
  32. #I am the wrapper around the decorated function.
  33. #I can access all the variables
  34. # - from the decorator: Leonard Sheldon
  35. # - from the function call: Rajesh Howard
  36. #Then I can pass them to the decorated function
  37. #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. @decorator_maker_with_arguments('Leonard', c1)
  4. def decorated_function_with_arguments(function_arg1, function_arg2):
  5. print ('I am the decorated function and only knows about my arguments:'
  6. ' {0} {1}'.format(function_arg1, function_arg2))
  7. decorated_function_with_arguments(c2, 'Howard')
  8. #outputs:
  9. #I make decorators! And I accept arguments: Leonard Penny
  10. #I am the decorator. Somehow you passed me arguments: Leonard Penny
  11. #I am the wrapper around the decorated function.
  12. #I can access all the variables
  13. # - from the decorator: Leonard Penny
  14. # - from the function call: Leslie Howard
  15. #Then I can pass them to the decorated function
  16. #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。但记住,装饰器只会被调用一次。只在 Python 导入脚本的时候运行。在这之后你就无法动态设置参数了。当你执行 import x 之后,函数已经被装饰了,因此之后你无法改变任何东西。


Let’s practice: decorating a decorator

练习: 装饰一个装饰器

Okay, 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. 这个函数是被用作装饰器。
  11. 它会装饰其他函数,被装饰的函数也是一个装饰器。
  12. 喝杯咖啡吧。
  13. 它允许任何装饰器接收任意个参数,
  14. 这样你就不会为每次都要考虑怎样处理而头疼了
  15. """
  16. # We use the same trick we did to pass arguments
  17. # 我们使用同样的技巧来传递参数
  18. def decorator_maker(*args, **kwargs):
  19. # We create on the fly a decorator that accepts only a function
  20. # but keeps the passed arguments from the maker.
  21. # 我们创建一个仅可以接收一个函数的临时装饰器
  22. # 但无法从 maker 传递参数
  23. def decorator_wrapper(func):
  24. # We return the result of the original decorator, which, after all,
  25. # IS JUST AN ORDINARY FUNCTION (which returns a function).
  26. # Only pitfall: the decorator must have this specific signature or it won’t work:
  27. # 原装饰器返回的结果
  28. # 其实只是一个普通函数(这个函数返回一个函数)。
  29. # 唯一的陷阱是: 装饰器必须有特定的格式,否则无法运行:
  30. return decorator_to_enhance(func, *args, **kwargs)
  31. return decorator_wrapper
  32. 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. # 创建一个用作装饰器的函数。然后加上一个装饰器 :-)
  4. # 不要忘记,格式是 `decorator(func, *args, **kwargs)`
  5. @decorator_with_args
  6. def decorated_decorator(func, *args, **kwargs):
  7. def wrapper(function_arg1, function_arg2):
  8. print 'Decorated with', args, kwargs
  9. return func(function_arg1, function_arg2)
  10. return wrapper
  11. # Then you decorate the functions you wish with your brand new decorated decorator.
  12. # 然后用全新的装饰器装饰你的函数。
  13. @decorated_decorator(42, 404, 1024)
  14. def decorated_function(function_arg1, function_arg2):
  15. print 'Hello', function_arg1, function_arg2
  16. decorated_function('Universe and', 'everything')
  17. #outputs:
  18. #Decorated with (42, 404, 1024) {}
  19. #Hello Universe and everything
  20. # 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: decorators

最佳实践: 装饰器

  • Decorators were introduced in Python 2.4, so be sure your code will be run on >= 2.4.
  • Decorators slow down the function call. Keep that in mind.
  • You cannot 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 decorated for all the code.
  • Decorators wrap functions, which can make them hard to debug. (This gets better from Python >= 2.5; see below.)
  • 装饰器在 Python 2.4 引进,因此确保你的代码运行的 Python 版本 >=2.4
  • 装饰器会拖慢函数调用速度。请牢记
  • 你无法解除装饰一个函数。 (确实 一些技巧可以创建允许解除装饰的装饰器,但是没人会使用它们。)因此一旦函数被装饰了,所有这个函数的代码就都装饰了。
  • 装饰器包装函数,会使得函数更难调试。 (从 Python >=2.5 有所好转;看下文。)

The functools module was introduced in Python 2.5. It includes the function functools.wraps(), which copies the name, module, and docstring of the decorated function to its wrapper.
functools 模块在 Python 2.5 引进。模块中包含了函数 functools.wraps() ,这个函数会把被装饰函数的名字,模块名,docstring 都复制到它的包装器中。

(Fun fact: functools.wraps() is a decorator! ☺)
(有趣的事情是: functools.wraps() 是个装饰器!☺)

  1. # For debugging, the stacktrace prints you the function __name__
  2. # 至于调试,stacktrace 输出函数的 __name__
  3. def foo():
  4. print 'foo'
  5. print foo.__name__
  6. #outputs: foo
  7. # With a decorator, it gets messy
  8. # 有了装饰器之后,有点混乱
  9. def bar(func):
  10. def wrapper():
  11. print 'bar'
  12. return func()
  13. return wrapper
  14. @bar
  15. def foo():
  16. print 'foo'
  17. print foo.__name__
  18. #outputs: wrapper
  19. # `functools` can help with that
  20. # `functools` 可以改善上面的情况
  21. import functools
  22. def bar(func):
  23. # We say that `wrapper`, is wrapping `func`
  24. # and the magic begins
  25. # 我们认为 `wrapper` 正在包装 `func`
  26. # 神奇的事情发生了
  27. @functools.wraps(func)
  28. def wrapper():
  29. print 'bar'
  30. return func()
  31. return wrapper
  32. @bar
  33. def foo():
  34. print 'foo'
  35. print foo.__name__
  36. #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 debugging (you don’t want to modify it because it’s temporary).
装饰器看起来很酷,很强大,但有一个实用的例子就更好了。大概有 1000 种可能的例子。常见的使用方法是扩展一个外部库函数(你无法修改)的行为,或者用来调试外部库函数(你不想修改它,因为它是临时函数)。

You can use them to extend several functions in a DRY’s way, like so:
你可以使用装饰器以 DRY(Don’t Repeat Yourself,不重复自己) 的方式扩展函数,就像这样:

  1. def benchmark(func):
  2. """
  3. A decorator that prints the time a function takes
  4. to execute.
  5. """
  6. """
  7. 一个用来输出函数执行时间的装饰器
  8. """
  9. import time
  10. def wrapper(*args, **kwargs):
  11. t = time.clock()
  12. res = func(*args, **kwargs)
  13. print func.__name__, time.clock()-t
  14. return res
  15. return wrapper
  16. def logging(func):
  17. """
  18. A decorator that logs the activity of the script.
  19. (it actually just prints it, but it could be logging!)
  20. """
  21. """
  22. 一个用来记录脚本活动的装饰器。
  23. (实际上只是打印出来,但可以输出到日志!)
  24. """
  25. def wrapper(*args, **kwargs):
  26. res = func(*args, **kwargs)
  27. print func.__name__, args, kwargs
  28. return res
  29. return wrapper
  30. def counter(func):
  31. """
  32. A decorator that counts and prints the number of times a function has been executed
  33. """
  34. """
  35. 一个用来统计并输出函数执行次数的装饰器
  36. """
  37. def wrapper(*args, **kwargs):
  38. wrapper.count = wrapper.count + 1
  39. res = func(*args, **kwargs)
  40. print '{0} has been used: {1}x'.format(func.__name__, wrapper.count)
  41. return res
  42. wrapper.count = 0
  43. return wrapper
  44. @counter
  45. @benchmark
  46. @logging
  47. def reverse_string(string):
  48. return str(reversed(string))
  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. #outputs:
  52. #reverse_string ('Able was I ere I saw Elba',) {}
  53. #wrapper 0.0
  54. #wrapper has been used: 1x
  55. #ablE was I ere I saw elbA
  56. #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!',) {}
  57. #wrapper 0.0
  58. #wrapper has been used: 2x
  59. #!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:
当然,装饰器的优点就在于你可以在不重写函数的前提下,使用在几乎任何函数上。DRY(Don’t Repeat Yourself,不要重复你自己),正如我说的:

  1. @counter
  2. @benchmark
  3. @logging
  4. def get_random_futurama_quote():
  5. from urllib import urlopen
  6. result = urlopen('http://subfusion.net/cgi-bin/quote.pl?quote=futurama').read()
  7. try:
  8. value = result.split('<br><b><hr><br>')[1].split('<br><br><hr>')[0]
  9. return value.strip()
  10. except:
  11. return 'No, I’m ... doesn’t!'
  12. print get_random_futurama_quote()
  13. print get_random_futurama_quote()
  14. #outputs:
  15. #get_random_futurama_quote () {}
  16. #wrapper 0.02
  17. #wrapper has been used: 1x
  18. #The laws of science be a harsh mistress.
  19. #get_random_futurama_quote () {}
  20. #wrapper 0.01
  21. #wrapper has been used: 2x
  22. #Curse you, merciful Poseidon!

Python itself provides several decorators: property, staticmethod, etc.
Python 本身提供了几种装饰器: propertystaticmethod,等

  • Django uses decorators to manage caching and view permissions.
  • Twisted to fake inlining asynchronous functions calls.
  • Django 使用装饰器来管理缓存,查看权限。
  • Twisted 用它来伪造内联异步函数调用。

This really is a large playground.
装饰器的用途确实很广。