原文出自stackoverflow,英文好的同学可以直接戳这里,如果是人人看的那么可能排版不太好,可以戳这里
Python的函数也是对象
为了理解装饰器,你必须首先要理解python里的函数也是对象。这是一个很重要的概念,我们可以来看个小例子:
def shout(word="yes"):
return word.capitalize()+"!"
print shout()
# 输出 : 'Yes!'
# 作为一个对象,你可以像其他对象一样,将他赋给一个变量
scream = shout
# 注意我们没有使用shout() 因为我们不是在调用shout这个函数,
# 我们把shout这个函数传递给给scream
# 这意味着我们可以调用scream来代替调用shoot
print scream()
# 输出 : 'Yes!'
# 不仅如此,你删掉shout, 仍然可以调用scream
del shout
try:
print shout()
except NameError, e:
print e
#outputs: "name 'shout' is not defined"
print scream()
# 输出: 'Yes!'
我们先把上面的概念记下,过会我们会再回来。
## Python里函数的另外一个特性:函数可以定义在另外一个函数内(函数中定义函数)
def talk():
# 你可以在函数“talk”中定义一个函数
def whisper(word="yes"):
return word.lower()+"..."
# 然后在“talk”函数里直接使用“whisper”
print whisper()
# 你调用了内部定义了“whisper”的talk函数,
# 每一次调用talk,talk中的whisper也会被调用
talk()
# 输出:
# "yes..."
# 但是在talk函数之外并不存在whisper:
try:
print whisper()
except NameError, e:
print e
# 输出 : "name 'whisper' is not defined"
##
##
函数引用
从上面的例子可以看出函数是对象,因此:
- 可以传递给变量
可以在另一个函数中定义
这意味着一个函数可以返回另外一个函数(返回值是一个函数),我们看一下例子:
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()
你可以看到这里的talk是一个函数对象
print talk
输出 : <function shout at 0xb7ea817c>
这个对象是函数返回的一个对象
print talk()
输出 : Yes!
你也可以直接用一种很难看的方式直接调用
print getTalk(“whisper”)()
输出 : yes…
既然你可以返回一个函数,那么你也可以传递一个函数对象作为参数
def doSomethingBefore(func):
print "I do something before then I call the function you gave me"
print func()
doSomethingBefore(scream)
# 输出:
# I do something before then I call the function you gave me
# Yes!
你应该已经能够理解装饰器了。装饰器就是一种包装,能够让你在不改变函数本身的情况下,在其之前或之后执行代码
## 手工实现装饰器
你会如何实现一个装饰器呢?
# 一个装饰器是一个能够接收另外一个函数作为参数的函数
def my_shiny_new_decorator(a_function_to_decorate):
# 装饰器在内部定义了一个函数:the_wrapper_around_the_original_function
# 这是一个对原始函数(a_function_to_decorate)进行包装的函数
# 所以它可以在原始函数(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"
# 在这时,a_function_todecorate还没有被执行(这是定义过程)
# 我们返回我们刚刚创建的装饰器函数:the_wrapper_around_the_original_function
# 这个装饰器包括了原函数以及我们在其之前和之后需要执行的代码
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()
# 输出: I am a stand alone function, don't you dare modify me
# 现在通过装饰它来扩展它的行为
# 只需要将它传给装饰器,它就会动态的被装饰
# 然后你会得到一个新的可以使用的函数
a_stand_alone_function_decorated = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function_decorated()
# 输出:
# Before the function runs
# I am a stand alone function, don't you dare modify me
# After the function runs
现在,你每次想调用a_stand_alone_function,都通过调用a_stand_alone_function_decorated来代替。
这个很好解决, 通过my_shiny_new_decorator返回的函数来重写a_stand_alone_function_decorated
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)
a_stand_alone_function()
#输出:
# Before the function runs
# I am a stand alone function, don't you dare modify me
# After the function runs# And guess what? That's EXACTLY what decorators do!
## 揭开装饰器的面纱(好吧,我承认这个副标题略二)
之前的例子,我们通过使用装饰器语法来实现:
@my_shiny_new_decorator
def another_stand_alone_function():
print "Leave me alone"
another_stand_alone_function()
#输出:
#Before the function runs
#Leave me alone
#After the function runs
就这没简单,
@decorator等同于:
another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)
装饰器是 设计模式中装饰者模式的python实现。这些经典的设计模式都 被内嵌在python中来简化开发,就像iterators(迭代器)一样。
当然,你可以同时使用多个装饰器(手动装饰器实现):
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()
#输出: --ham--
sandwich = bread(ingredients(sandwich))
sandwich()
#输出:
#</''''''>
# #tomatoes#
# --ham--
# ~salad~
#<______/>
使用python语法糖实现:
@bread
@ingredients
def sandwich(food="--ham--"):
print food
sandwich()
#输出:
#</''''''>
# #tomatoes#
# --ham--
# ~salad~
#<______/>
The order you set the decorators MATTERS:
@ingredients
@bread
def strange_sandwich(food="--ham--"):
print food
strange_sandwich()
#输出:
##tomatoes#
#</''''''>
# --ham--
#<______/>
# ~salad~
接下来我们来看一些关于装饰器的高级用法
向被装饰的函数传递参数
# 这不是什么黑魔法,你只需像装饰器传递参数
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_wrapper_accepting_arguments这个函数,
# 把参数传递给wrapper然后再让它把参数传递给被装饰的原函数
@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")
# 输出:
#I got args! Look: Peter Venkman
#My name is Peter Venkman
##
##
装饰对象的方法
在Python中对象和方法是很相似的,不同之处在于方法需要将当前对象的因作为第一个参数传递进去。这意味考虑到self之后,你用相同的办法可以为方法创建装饰器,
def method_friendly_decorator(method_to_decorate):
def wrapper(self, lie):
lie = lie - 3 # very friendly, decrease age even more :-)
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)
#输出: I am 26, what did you think?
当然,如果你想创建一个通用与函数(function)与方法(method)的装饰器,不需要考虑参数如何,只要用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
# 然后解压缩参数,在这是*args和**kwargs
# 如果你对压缩和解压缩参数不熟悉,
# 看这:http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
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()
#输出:
#Do I have args?:
#()
#{}
#Python is cool, no argument here.
@a_decorator_passing_arbitrary_arguments
def function_with_arguments(a, b, c):
print a, b, c
function_with_arguments(1,2,3)
#输出:
#Do I have args?:
#(1, 2, 3)
#{}
#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!”)
#输出:
#Do I have args ? :
#(‘Bill’, ‘Linus’, ‘Steve’)
#{‘platypus’: ‘Indeed!’}
#Do Bill, Linus and Steve like platypus? Indeed!
class Mary(object):
def __init__(self):
self.age = 31
@a_decorator_passing_arbitrary_arguments
def sayYourAge(self, lie=-3): # You can now add a default value
print "I am %s, what did you think ?" % (self.age + lie)
m = Mary()
m.sayYourAge()
#输出:
Do I have args?:
#(<main.Mary object at 0xb7d303ac>,)
#{}
#I am 28, what did you think?
向装饰器传递参数
现在你可能想知道如何向装饰器本身传递参数。你可能不解为什么要这么做,因为之前的装饰器必须接受一个函数作为参数,但不能把被装饰的函数直接传递给装饰器
# 装饰器是个普通的函数
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)
输出: I am a ordinary function
它输出 “I am a ordinary function”, 因为你调用了它
@my_decorator
def lazy_function():
print “zzzzzzzz”
输出: I am a ordinary function
my_decorator的调用和使用@my_decorator是十分相似的。你让通过被变量标志的my_decorator来让python调用它。这很重要,因为这个标志可以让你直接指出装饰器
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()
输出:
I make decorators! I am executed only once: when you make me create a decorator
As a decorator maker, I return a decorator
然后我们来装饰一个函数
def decorated_function():
print “I am the decorated function.”
decorated_function = new_decorator(decorated_function)
outputs:
I am a decorator! I am executed only when you decorate a function.
As the decorator, I return the wrapped function
我们调用一下这个被装饰过的方法:
decorated_function()
#输出:
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.
I am the decorated function.
再让我们跳过中间变量来实现同样的事情
def decorated_function():
print “I am the decorated function.”
decorated_function = decorator_maker()(decorated_function)
输出:
I make decorators! I am executed only once: when you make me create a decorator
As a decorator maker, I return a decorator
I am a decorator! I am executed only when you decorate a function.
As the decorator, I return the wrapped function.
最后:
decorated_function()
输出:
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.
I am the decorated function.
让我们通过更简洁方式来实现
@decorator_maker()
def decorated_function():
print “I am the decorated function.”
#输出:
#I make decorators! I am executed only once: when you make me create a decorator.
#As a decorator maker, I return a decorator
#I am a decorator! I am executed only when you decorate a function.
#As the decorator, I return the wrapped function.
#然后就到最后了,调用:
decorated_function()
#outputs:
#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.
#I am the decorated function.
你觉得怎么样,我们通过@来进行函数调用
回到向装饰器传递参数这个问题上。如果我们使用函数来产生装饰器,我们也可以向这个函数传参
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):
# 向这传递参数是一种闭包特性
# 如果你不喜欢这种闭包特性,你可以把它传给一个变量
# 或者看看这:http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python
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 variablesn"
"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”)
#输出:
#I make decorators! And I accept arguments: Leonard Sheldon
#I am the decorator. Somehow you passed me arguments: Leonard Sheldon
#I am the wrapper around the decorated function.
#I can access all the variables
- from the decorator: Leonard Sheldon
- from the function call: Rajesh Howard
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Rajesh Howard
像这样,一个需要参数的装饰器,参数可以以变量的形式传递进去:
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”)
#输出:
#I make decorators! And I accept arguments: Leonard Penny
#I am the decorator. Somehow you passed me arguments: Leonard Penny
#I am the wrapper around the decorated function.
#I can access all the variables
- from the decorator: Leonard Penny
- from the function call: Leslie Howard
#Then I can pass them to the decorated function
#I am the decorated function and only knows about my arguments: Leslie Howard
做个练习:一个用来装饰装饰器的装饰器
def decorator_with_args(decorator_to_enhance):
# 我们像之前那样传递参数
def decorator_maker(*args, **kwargs):
# 创建一个只接受函数作为参数的函数
# 但是能够使用maker传递的参数
def decorator_wrapper(func):
# 我们将返回原始的装饰器,就像返回一个普通的函数一样
# IS JUST AN ORDINARY FUNCTION (which returns a function).
# 需要注意的是装饰器必须有自己的标识符,否则无法工作
return decorator_to_enhance(func, *args, **kwargs)
return decorator_wrapper
return decorator_maker</pre>
然后可以这么使用:
# 你创造了了一个将作为装饰器使用的函数,然后向里面插入了一个装饰器
别忘了,插入的装饰器的标识符是 “decorator(func, args, *kwargs)”
@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”)
#输出:
#Decorated with (42, 404, 1024) {}
#Hello Universe and everything
酷不?
我知道,你上次有这种感觉,应该是听到别人说:“要理解递归,你要先理解递归”的时候。但是掌握了刚才讲的这些之后是不是很爽。
##
装饰器范例
@bar
def foo():
print “foo”
print foo.name
#outputs: wrapper
import functools
def bar(func):
# 我们说的包装器(wrapper)是用来包装func的
# 下面是见证奇迹的时刻
@functools.wraps(func)
def wrapper():
print "bar"
return func()
return wrapper
@bar
def foo():
print “foo”
print foo.name
#输出: foo
如何有效的使用装饰器
也许你会有个疑问:我可以用装饰器做什么,它看起来很好很强大,但是如果有个实际例子会更好。
关于在什么情况下使用有很多种可能,传统的用法是当你需要扩展一个外部库函数的行为或者处于debug的需要(但你不想对其直接做做更改,因为这只是临时的),你可以使用一个装饰器来扩展许多函数,不要重写每个函数(DRY原则),举个例子:
def benchmark(func):
“””
A decorator that prints the time a function takes
to execute.
“””
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):
“””
A decorator that logs the activity of the script.
(it actually just prints it, but it could be logging!)
“””
def wrapper(args, **kwargs):
res = func(args, **kwargs)
print func.name, args, kwargs
return res
return wrapper
def counter(func):
“””
A decorator that counts and prints the number of times a function has been executed
“””
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!”)
#输出:
#reverse_string (‘Able was I ere I saw Elba’,) {}
#wrapper 0.0
#wrapper has been used: 1x
#ablE was I ere I saw elbA
#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!’,) {}
#wrapper 0.0
#wrapper has been used: 2x
#!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
而使用装饰器的好处是是不需要做多余的重写就能立刻使用:
@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()
#输出:
#get_random_futurama_quote () {}
#wrapper 0.02
#wrapper has been used: 1x
#The laws of science be a harsh mistress.
#get_random_futurama_quote () {}
#wrapper 0.01
#wrapper has been used: 2x
#Curse you, merciful Poseidon!
Python本身提供了许多装饰器:property, staticmethod等等。Django使用装饰器来捕获和查看权限等,装饰器用途广泛
原文出自:http://stackoverflow.com/questions/739ddd654/how-can-i-make-a-chain-of-function-decorators-in-python/1594484#1594484