deftalk():# 你可以在 'talk' 里动态的(on the fly)定义一个函数... defwhisper(word="yes"):return word.lower()+"..."# ... 然后马上调用它! print whisper() # 每当调用'talk',都会定义一次'whisper',然后'whisper'在'talk'里被调用 talk() # outputs: # "yes..." # 但是"whisper" 在 "talk"外并不存在: try: print whisper() except NameError, e: print e #outputs : "name 'whisper' is not defined"*
函数引用(Functions references)
你刚刚已经知道了,python的函数也是对象,因此:
可以被赋值给变量
可以在另一个函数体内定义
那么,这样就意味着一个函数可以返回另一个函数 :-),来看个例子:
defgetTalk(type="shout"):# 我们先动态定义一些函数 defshout(word="yes"):return word.capitalize()+"!"defwhisper(word="yes") :return word.lower()+"..."; # 然后返回其中一个 if type == "shout": # 注意:我们是在返回函数对象,而不是调用函数, # 所以不要用到括号 "()" return shout else: return whisper # 那你改如何使用这个怪兽呢?(How do you use this strange beast?) # 先把函数赋值给一个变量 talk = getTalk() # 你可以发现 "talk" 其实是一个函数对象: print talk #outputs : # 这个对象就是 getTalk 函数返回的: print talk() #outputs : Yes! # 你甚至还可以直接这样使用(if you feel wild): print getTalk("whisper")() #outputs : yes...
既然可以返回一个函数,那么也就可以像参数一样传递:
defdoSomethingBefore(func):print"I do something before then I call the function you gave me"print func() doSomethingBefore(scream) #outputs: #I do something before then I call the function you gave me #Yes!
# 一个装饰器是一个需要另一个函数作为参数的函数 defmy_shiny_new_decorator(a_function_to_decorate):# 在装饰器内部动态定义一个函数:wrapper(原意:包装纸). # 这个函数将被包装在原始函数的四周 # 因此就可以在原始函数之前和之后执行一些代码. defthe_wrapper_around_the_original_function():# 把想要在调用原始函数前运行的代码放这里 print"Before the function runs"# 调用原始函数(需要带括号) a_function_to_decorate() # 把想要在调用原始函数后运行的代码放这里 print"After the function runs"# 直到现在,"a_function_to_decorate"还没有执行过 (HAS NEVER BEEN EXECUTED). # 我们把刚刚创建的 wrapper 函数返回. # wrapper 函数包含了这个函数,还有一些需要提前后之后执行的代码, # 可以直接使用了(It's ready to use!) return the_wrapper_around_the_original_function # Now imagine you create a function you don't want to ever touch again. defa_stand_alone_function():print"I am a stand alone function, don't you dare modify me" a_stand_alone_function() #outputs: 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() #outputs: #Before the function runs #I am a stand alone function, don't you dare modify me #After the function runs
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function) a_stand_alone_function() #outputs: #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!
揭秘装饰器(Decorators demystified)
我们用装饰器的语法来重写一下前面的例子:
@my_shiny_new_decorator defanother_stand_alone_function():print"Leave me alone" another_stand_alone_function() #outputs: #Before the function runs #Leave me alone #After the function runs
现在对于给装饰器本身传参数,你有什么看法呢?好吧,这样说有点绕,因为装饰器必须接受一个函数作为参数,所以就不能把被装饰的函数的参数,直接传给装饰器(you cannot pass the decorated function arguments directly to the decorator.)
在直奔答案之前,我们先写一个小提示:
这完全一样,都是 my_decorator 被调用。所以当你使用 @my_decorator 时,你在告诉 python 去调用 “被变量 my_decorator 标记的” 函数(the function ‘labeled by the variable “my_decorator”‘)。这很重要,因为你给的这个标签能直接指向装饰器或者其他!
不要感到惊讶,让我们做一件完全一样的事情,只不过跳过了中间变量:
defdecorated_function():print"I am the decorated function." decorated_function = decorator_maker()(decorated_function) #outputs: #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. # Finally: 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.
再做一次,代码甚至更短:
@decorator_maker() defdecorated_function():print"I am the decorated function." #outputs: #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. #Eventually: 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.
我们在用 @ 语法调用了函数 , 那么回到带参数的装饰器。如果我们能够使用一个函数动态(on the fly)的生成装饰器,那么我们就能把参数传递给那个函数,对吗?
defdecorator_maker_with_arguments(decorator_arg1, decorator_arg2):print"I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2 defmy_decorator(func):# 在这里能传参数是一个来自闭包的馈赠. # 如果你对闭包感到不舒服,你可以直接忽略(you can assume it's ok), # 或者看看这里: 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 # 不要把装饰器参数和函数参数搞混了! defwrapped(function_arg1, function_arg2) :print ("I am the wrapper around the decorated function.
""I can access all the variables
"" - from the decorator: {0} {1}
"" - from the function call: {2} {3}
""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") defdecorated_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") #outputs: #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
这就是了,带参数的装饰器。参数也可以设置为变量:
如你所见,你可以给装饰器传递参数,就好像其他任意一个使用了这种把戏的函数一样(。如果你愿意,甚至可以使用 *args, **kwargs。但是,记住,装置器只调用一次,仅当python导入这个脚本时。你不能在之后动态的设置参数。当你执行 import x 时,这个函数已经被装饰了,因此你不能修改任何东西。
实践:装饰器装饰一个装饰器(Let’s practice: a decorator to decorate a decorator)
defdecorator_with_args(decorator_to_enhance):""" This function is supposed to be used as a decorator. It must decorate an other function, that is intended to be used as a decorator. Take a cup of coffee. It will allow any decorator to accept an arbitrary number of arguments, saving you the headache to remember how to do that every time. """# We use the same trick we did to pass arguments defdecorator_maker(*args, **kwargs):# We create on the fly a decorator that accepts only a function # but keeps the passed arguments from the maker. defdecorator_wrapper(func):# We return the result of the original decorator, which, after all, # IS JUST AN ORDINARY FUNCTION (which returns a function). # Only pitfall: the decorator must have this specific signature or it won't work: return decorator_to_enhance(func, *args, **kwargs) return decorator_wrapper return decorator_maker
它可以像这样使用:
# You create the function you will use as a decorator. And stick a decorator on it :-) # Don't forget, the signature is "decorator(func, *args, **kwargs)" @decorator_with_args defdecorated_decorator(func, *args, **kwargs):defwrapper(function_arg1, function_arg2):print"Decorated with", args, kwargs return func(function_arg1, function_arg2) return wrapper # Then you decorate the functions you wish with your brand new decorated decorator. @decorated_decorator(42, 404, 1024) defdecorated_function(function_arg1, function_arg2):print"Hello", function_arg1, function_arg2 decorated_function("Universe and", "everything") #outputs: #Decorated with (42, 404, 1024) {} #Hello Universe and everything # Whoooot!
defbenchmark(func):""" A decorator that prints the time a function takes to execute. """import time defwrapper(*args, **kwargs): t = time.clock() res = func(*args, **kwargs) print func.__name__, time.clock()-t return res return wrapper deflogging(func):""" A decorator that logs the activity of the script. (it actually just prints it, but it could be logging!) """defwrapper(*args, **kwargs): res = func(*args, **kwargs) print func.__name__, args, kwargs return res return wrapper defcounter(func):""" A decorator that counts and prints the number of times a function has been executed """defwrapper(*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 = 0return wrapper @counter
@benchmark logging
defreverse_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!") #outputs: #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