摘要:為了避免重復(fù)調(diào)用,可以適當(dāng)?shù)刈鼍彺妫难b飾器可以完美的完成這一任務(wù)。這意味著我們可以為方法創(chuàng)建裝飾器,只是要記得考慮。裝飾器封裝了函數(shù),這使得調(diào)試函數(shù)變得困難。另外,使用裝飾器去管理緩存和權(quán)限。
原文地址
之前用python簡單寫了一下斐波那契數(shù)列的遞歸實(shí)現(xiàn)(如下),發(fā)現(xiàn)運(yùn)行速度很慢。
def fib_direct(n): assert n > 0, "invalid n" if n < 3: return 1 else: return fib_direct(n - 1) + fib_direct(n - 2)
然后大致分析了一下fib_direct(5)的遞歸調(diào)用過程,如下圖:
可以看到多次重復(fù)調(diào)用,因此效率十分低。進(jìn)一步,可以算出遞歸算法的時間復(fù)雜度。T(n) = T(n-1) + T(n-2),用常系數(shù)線性齊次遞推方程的解法,解出遞推方程的特征根,特征根里最大的n次方就是它的時間復(fù)雜度O(1.618^n),指數(shù)級增長。
為了避免重復(fù)調(diào)用,可以適當(dāng)?shù)刈鼍彺妫琾ython的裝飾器可以完美的完成這一任務(wù)。
裝飾器:基礎(chǔ)python中一切都是對象,這里需要強(qiáng)調(diào)函數(shù)是對象。為了更好地理解函數(shù)也是對象,下面結(jié)合代碼片段來說明這一點(diǎn)。
def shout(word="yes"): return word.capitalize() + "!" print shout() # outputs: Yes! """ As an object, you can assign the function to a variable like any other object. Notice we don"t use parentheses: we are not calling the function, we are putting the function "shout" into the variable "scream". """ scream = shout print scream() # outputs: Yes! """ More than that, it means you can remove the old name "shout", and the function will still be accessible from "scream". """ del shout try: print shout() except NameError, e: print e # outputs: name "shout" is not defined print scream() # outputs: "Yes!"
因?yàn)楹瘮?shù)是對象,所以python中函數(shù)還有一個有趣的特性:函數(shù)可以被定義在另一個函數(shù)中。下面來看一個簡單的例子。
def talk(): # You can define a function on the fly in "talk" def whisper(word="yes"): return word.lower()+"..." print whisper() """ You call "talk", that defines "whisper" EVERY TIME you call it, then "whisper" is called in "talk". """ talk() # outputs: yes... # But "whisper" DOES NOT EXIST outside "talk". try: print whisper() except NameError, e: print e # outputs : name "whisper" is not defined函數(shù)引用
前面已經(jīng)知道函數(shù)是對象。那么:
可以被賦給另一個變量
可以被定義在另一個函數(shù)里
這也意味著,一個函數(shù)可以返回另一個函數(shù),下面看一個簡單的例子。
def get_talk(kind="shout"): def whisper(word="yes"): return word.lower() + "..." def shout(word="yes"): return word.capitalize() + "!" return whisper if kind == "whisper" else shout # Get the function and assign it to a variable talk = get_talk() # You can see that "talk" is here a function object: print talk # outputs :print talk() # outputs : Yes! # And you can even use it directly if you feel wild: print get_talk("whisper")() # outputs : yes...
我們來進(jìn)一步挖掘一下函數(shù)的特性,既然可以返回函數(shù),那么我們也可以把函數(shù)作為參數(shù)傳遞。
def whisper(word="yes"): return word.lower() + "..." def do_something_before(func): print "I do something before." print "Now the function you gave me: ", func() do_something_before(whisper) """outputs I do something before. Now the function you gave me: yes... """
現(xiàn)在,了解裝飾器所需要的所有要點(diǎn)我們已經(jīng)掌握了,通過上面的例子,我們還可以看出,裝飾器其實(shí)就是封裝器,可以讓我們在不修改原函數(shù)的基礎(chǔ)上,在執(zhí)行原函數(shù)的前后執(zhí)行別的代碼。
手工裝飾器下面我們手工實(shí)現(xiàn)一個簡單的裝飾器。
def my_shiny_new_decorator(a_function_to_decorate): """ Inside, the decorator defines a function on the fly: the wrapper. This function is going to be wrapped around the original function so it can execute code before and after it. """ def the_wrapper_around_the_original_function(): """ Put here the code you want to be executed BEFORE the original function is called """ print "Before the function runs" # Call the function here (using parentheses) a_function_to_decorate() """ Put here the code you want to be executed AFTER the original function is called """ print "After the function runs" """ At this point, "a_function_to_decorate" HAS NEVER BEEN EXECUTED. We return the wrapper function we have just created. The wrapper contains the function and the code to execute before and after. 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. def a_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 """ Well, you can decorate it to extend its behavior. Just pass it to the decorator, it will wrap it dynamically in any code you want and return you a new function ready to be used: """ 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 """
現(xiàn)在,如果我們想每次調(diào)用a_stand_alone_function的時候,實(shí)際上調(diào)用的是封裝后的函數(shù)a_stand_alone_function_decorated,那么只需要用a_stand_alone_function去覆蓋my_shiny_new_decorator返回的函數(shù)即可。也就是:
a_stand_alone_function = my_shiny_new_decorator(a_stand_alone_function)裝飾器闡述
對于前面的例子,如果用裝飾器語法,可以添加如下:
@my_shiny_new_decorator def another_stand_alone_function(): print "Leave me alone" another_stand_alone_function() """outputs: Before the function runs Leave me alone After the function runs """
對了,這就是裝飾器語法,這里的@my_shiny_new_decorator是another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)的簡寫。
裝飾器只是裝飾器設(shè)計模式的python實(shí)現(xiàn),python還存在其他幾個經(jīng)典的設(shè)計模式,以方便開發(fā),例如迭代器iterators。
當(dāng)然了,我們也可以嵌套裝飾器。
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() # outputs: --ham-- sandwich = bread(ingredients(sandwich)) sandwich() """outputs: """"""> #tomatoes# --ham-- ~salad~ <\______/> """
用python的裝飾器語法,如下:
@bread @ingredients def sandwich_2(food="--ham_2--"): print food sandwich_2()
放置裝飾器的位置很關(guān)鍵。
@ingredients @bread def strange_sandwich(food="--ham--"): print food strange_sandwich() """outputs: #tomatoes# """"""> --ham-- <\______/> ~salad~ """裝飾器高級用法 給裝飾器函數(shù)傳遞參數(shù)
當(dāng)我們調(diào)用裝飾器返回的函數(shù)時,其實(shí)是在調(diào)用封裝函數(shù),給封裝函數(shù)傳遞參數(shù)也就同樣的給被裝飾函數(shù)傳遞了參數(shù)。
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 """ Since when you are calling the function returned by the decorator, you are calling the wrapper, passing arguments to the wrapper will let it pass them to the decorated function """ @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") """outputs: I got args! Look: Peter Venkman My name is Peter Venkman """裝飾方法
python中函數(shù)和方法幾乎一樣,除了方法中第一個參數(shù)是指向當(dāng)前對象的引用(self)。這意味著我們可以為方法創(chuàng)建裝飾器,只是要記得考慮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) # outputs: I am 26, what did you think?
我們還可以創(chuàng)建一個通用的裝飾器,可以用于所有的方法或者函數(shù),而且不用考慮它的參數(shù)情況。這時候,我們要用到*args, **kwargs。
def a_decorator_passing_arbitrary_arguments(function_to_decorate): # The wrapper accepts any arguments def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs): print "Do I have args?:" print args print kwargs # Then you unpack the arguments, here *args, **kwargs # If you are not familiar with unpacking, check: # 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
另外還有一些高級用法,這里不做詳細(xì)說明,可以在How can I make a chain of function decorators in Python?進(jìn)一步深入了解裝飾器。
functools.wraps裝飾器封裝了函數(shù),這使得調(diào)試函數(shù)變得困難。不過在python 2.5引入了functools模塊,它包含了functools.wraps()函數(shù),這個函數(shù)可以將被封裝函數(shù)的名稱、模塊、文檔拷貝給封裝函數(shù)。有趣的是,functools.wraps是一個裝飾器。為了更好地理解,看以下代碼:
# For debugging, the stacktrace prints you the function __name__ def foo(): print "foo" print foo.__name__ # outputs: foo def bar(func): def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ # outputs: wrapper import functools def bar(func): # We say that "wrapper", is wrapping "func" # and the magic begins @functools.wraps(func) def wrapper(): print "bar" return func() return wrapper @bar def foo(): print "foo" print foo.__name__ # outputs: foo為何裝飾器那么有用
讓我們回到本篇文章開始的問題上,重復(fù)調(diào)用導(dǎo)致遞歸的效率低下,因此考慮使用緩存機(jī)制,空間換時間。這里,就可以使用裝飾器做緩存,看下面代碼:
from functools import wraps def cache(func): caches = {} @wraps(func) def wrap(*args): if args not in caches: caches[args] = func(*args) return caches[args] return wrap @cache def fib_cache(n): assert n > 0, "invalid n" if n < 3: return 1 else: return fib_cache(n - 1) + fib_cache(n - 2)
這樣遞歸中就不會重復(fù)調(diào)用,效率也會提高很多。具體可以看這里,從執(zhí)行時間很容易看出做了緩存之后速度有了很大的提升。裝飾器還可以用來擴(kuò)展外部接口函數(shù)(通常你不能修改它)的功能,或者用來調(diào)試函數(shù)。其實(shí),裝飾器可以用于各種各樣的場合!
python本身提供了一些裝飾器:property,staticmethod,等等。另外,Django使用裝飾器去管理緩存和權(quán)限。
參考計算斐波納契數(shù),分析算法復(fù)雜度
How can I make a chain of function decorators in Python?
Python裝飾器與面向切面編程
how to use args and kwargs in python?
Fibonacci, recursion and decorators
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/45281.html
摘要:概括的講,裝飾器的作用就是為已經(jīng)存在的函數(shù)或?qū)ο筇砑宇~外的功能。在理解這些裝飾器之前,最好對函數(shù)的閉包和裝飾器的接口約定有一定了解。是一個非常簡單的裝飾器加強(qiáng)包。 Python中的裝飾器是你進(jìn)入Python大門的一道坎,不管你跨不跨過去它都在那里。 為什么需要裝飾器 我們假設(shè)你的程序?qū)崿F(xiàn)了say_hello()和say_goodbye()兩個函數(shù)。 def say_hello(): ...
摘要:今天我們一起探討一下裝飾器的另類用法。語法回顧開始之前我們再將裝飾器的語法回顧一下。例子本身只是演示了裝飾器的一種用法,但不是推薦你就這樣使用裝飾器。類裝飾器在以前,還不支持類裝飾器。 之前有比較系統(tǒng)介紹過Python的裝飾器(請查閱《詳解Python裝飾器》),本文算是一個補(bǔ)充。今天我們一起探討一下裝飾器的另類用法。 語法回顧 開始之前我們再將Python裝飾器的語法回顧一下。 @d...
小編寫這篇文章的主要目的,主要是來給大家介紹,關(guān)于python中,相關(guān)語法問題的解答,比如在python,我們會遇到閉包和裝飾器不會用的情況,那么,下文就會來給大家做一個詳細(xì)的解答。 *args與**kwarsg及閉包和裝飾器 過程 先理解閉包,再理解裝飾器,不要忘了不定長參數(shù) deffunc(): msg='111' deffunc1(): print(ms...
摘要:在學(xué)習(xí)裝飾器語法之前,需要先溫習(xí)一下的一些基礎(chǔ)知識。函數(shù)最后必須返回。使用時也很簡單,如下在方法前面加上,就是裝飾器語法。裝備了,攻擊更強(qiáng)了。職業(yè)的基本攻擊穿上了,移動速度更快了。 在學(xué)習(xí)ES7裝飾器語法之前,需要先溫習(xí)一下ES5的一些基礎(chǔ)知識。 假設(shè)有對象如下:(便于理解) var person = { name: TOM } 在ES5中,對象中的每個屬性都有一個特性值來描述...
閱讀 1816·2019-08-30 15:55
閱讀 1007·2019-08-26 11:57
閱讀 508·2019-08-26 11:29
閱讀 3358·2019-08-26 10:49
閱讀 1910·2019-08-23 18:40
閱讀 1749·2019-08-23 16:04
閱讀 3104·2019-08-23 11:01
閱讀 2271·2019-08-23 10:56