您好,登錄后才能下訂單哦!
這篇文章主要介紹“Python函數的介紹以及裝飾器入門用法”,在日常操作中,相信很多人在Python函數的介紹以及裝飾器入門用法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Python函數的介紹以及裝飾器入門用法”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
Python允許你,作為程序員,使用函數完成一些很酷的事情。在Python編程學習中,函數是一等對象(first-class object),這就意味著你可以像使用字符串,整數,或者任何其他對象一樣使用函數。
例如,你可以將函數賦值給變量:
>>> def square(n): ... return n * n; >>> square(4) 16 >>> alias = square >>> alias(4) 16
然而,一等函數的真正威力在于你可以把函數傳給其他函數,或者從其他函數中返回函數。Python的內置函數map利用了這種能力:給map傳個函數以及一個列表,它會依次以列表中每個元素為參數調用你傳給它的那個函數,從而生成一個新的列表。如下所示的例子中應用了上面的那個square函數:
>>> number = [1, 2, 3, 4, 5] >>> map(square, numbers) [1, 4, 9, 16, 25]
如果一個函數接受其他函數作為參數,以及/或者返回一個函數,那么它就被稱為高階函數 。雖然map函數只是簡單地使用了我們傳給它的函數,而沒有改變這個函數,但我們也可以使用高階函數去改變其他函數的行為。
例如,假設有這樣一個函數,會被調用很多次,以致運行代價非常昂貴:
>>> def fib(n): ... "Recursively (i.e., dreadfully) calculate the nth Fibonacci number." ... return n if n in [0, 1] else fib(n - 2) + fib(n - 1)
我們一般會保存計算過程中每次遞歸調用的結果,這樣,對于函數調用樹中經常出現某個n,當需要計算n對應的結果時,就不需要重復計算了。有多種方式可以做到這點。例如,我們可以將這些結果存在一個字典中,當以某個值為參數調用fib函數時,就先到這個字典去查一下其結果是否已經計算出來了。
但這樣的話,每次我們想要調用fib函數,都需要重復那段相同的字典檢查樣板式代碼。相反,如果讓fib函數自己在內部負責存儲其結果,那么在其他代碼中調用fib,就非常方便,只要簡單地調用它就行了。這樣一種技術被稱為memoization(注意沒有字母r的哦)。
我們可以把這種memoization代碼直接放入fib函數,但是Python為我們提供了另外一種更加優雅的選擇。因為可以編寫修改其他函數的函數,那么我們可以編寫一個通用的memoization函數,以一個函數作為參數,并返回這個函數的memoization版本:
def memoize(fn): stored_results = {} def memoized(*args): try: # try to get the cached result return stored_results[args] except KeyError: # nothing was cached for those args. let's fix that. result = stored_results[args] = fn(*args) return result return memoized
如上, memoize 函數以另一個函數作為參數,函數體中創建了一個字典對象用來存儲函數調用的結果:鍵為被memoized包裝后的函數的參數,值為以鍵為參數調用函數的返回值。 memoize 函數返回一個新的函數,這個函數會首先檢查在 stored_results 字典中是否存在與當前參數對應的條目;如果有,對應的存儲值會被返回;否則,就調用經過包裝的函數,存儲其返回值,并且返回給調用者。memoize返回的這種新函數常被稱為"包裝器"函數,因為它只是另外一個真正起作用的函數外面的一個薄層。
很好,現在有了一個memoization函數,我們可以把fib函數傳給它,從而得到一個經過包裝的fib,這個版本的fib函數不需要重復以前那樣的繁重工作:
def fib(n): return n if n in [0, 1] else fib(n - 2) + fib(n - 1) fib = memoize(fib)
通過高階函數memoize,我們獲得了memoization帶來的好處,并且不需要對fib函數自己做出任何改變,以免夾雜著memoization的代碼而模糊了函數的實質工作。但是,你也許注意到上面的代碼還算有點別扭,因為我們必須寫3遍fib。由于這種模式-傳遞一個函數給另一個函數,然后將結果返回給與原來那個函數同名的函數變量-在使用包裝器函數的代碼中極為常見,Python為其提供了一種特殊的語法:裝飾器:
@memoize def fib(n): return n if n in [0, 1] else fib(n - 2) + fib(n -1)
這里,我們說memoize函數裝飾了fib函數。需要注意的是這僅是一種語法上的簡便寫法(譯注:就是我們常說的"語法糖")。這段代碼與前面的代碼片段做的是同樣的事情:定義一個名為fib的函數,把它傳給memoize函數,將返回結果存為名為fib的函數變量。特殊的(看起來有點奇怪的)@語法只是減少了冗余。
你可以將多個裝飾器堆疊起來使用,它們會自底向上地逐個起作用。例如,假設我們還有另一個用來幫助調試的高階函數:
def make_verbose(fn): def verbose(*args): # will print (e.g.) fib(5) print '%s(%s)' % (fb.__name__, ', '.join(repr(arg) for arg in args)) return fn(*args) # actually call the decorated function return verbose
下面的兩個代碼片段做的是同樣的事情:
@memoize @make_verbose def fib(n): return n if n in [0, 1] else fib(n - 2) + fib(n - 1) def fib(n): return n if n in [0, 1] else fib(n - 2) + fib(n - 1) fib = memoize(make_verbose(fib))
有趣的是,Python并沒有限制你在@符號后只能寫一個函數名:你也可以調用一個函數,從而能夠高效地傳遞參數給裝飾器。假設我們并不滿足于簡單的memoization,還想將函數的結果存儲到memcached中。如果你已經寫了一個 memcached 裝飾器函數,那么可以(例如)傳遞一個服務器地址給它:
@memcached('127.0.0.1:11211') def fib(n): return n if n in [0, 1] else fib(n - 2) + fib(n - 1)
非裝飾器語法的寫法會如下展開:
fib = memcached('127.0.0.1:11211')(fib)
Python配備有一些作為裝飾器使用的非常有用的函數。例如,Python有一個 classmethod 函數,可以創建大致類似于java的靜態方法:
class Foo(object): SOME_CLASS_CONSTANT = 42 @classmethod def add_to_my_constant(cls, value): # Here, `cls` will just be Foo, buf if you called this method on a # subclass of Foo, `cls` would be that subclass instead. return cls.SOME_CLASS_CONSTANT + value Foo.add_to_my_constant(10) # => 52 # unlike in Java, you can also call a classmethod on an instance f = Foo() f.add_to_my_constant(10) # => 52
旁注:文檔字符串
Python函數可以包含更多的信息,而不僅僅是代碼:它們也包含有用的幫助信息,比如函數名稱,文檔字符串:
>>> def fib(n): ... "Recursively (i.e., dreadfully) calculate the nth Fibonacci number." ... return n if n in [0, 1] else fib(n - 2) + fib(n - 1) ... >>> fib.__name__ 'fib' >>> fib.__doc__ 'Recursively (i.e., dreadfully) calculate the nth Fibonacci number.'
Python內置函數help輸出的就是這些信息。但是,當函數被包裝之后,我們看到就是包裝器函數的名稱和文檔字符串了:
>>> fib = memoized(fib) >>> fib.__name__ 'memoized' >>> fib.__doc__
那樣的信息并沒有什么用處。幸運的是,Python包含一個名為 functools.wraps 的助手函數,能夠把函數的幫助信息拷貝到其包裝器函數:
import functools def memoize(fn): stored_results = {} @functools.wraps(fn) def memoized(*args): # (as before) return memoized
使用裝飾器幫助你編寫裝飾器會使很多事情令人非常滿意。現在,如果使用更新過的memoize函數重試前面的代碼,我們將會看到得到保留的文檔:
>>> fib = memoized(fib) >>> fib.__name__ 'fib' >>> fib.__doc__ 'Recursively (i.e., dreadfully) calculate the nth Fibonacci number.'
到此,關于“Python函數的介紹以及裝飾器入門用法”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。