您好,登錄后才能下訂單哦!
這篇文章主要介紹“Python函數(shù)的介紹以及裝飾器入門用法”,在日常操作中,相信很多人在Python函數(shù)的介紹以及裝飾器入門用法問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Python函數(shù)的介紹以及裝飾器入門用法”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
Python允許你,作為程序員,使用函數(shù)完成一些很酷的事情。在Python編程學(xué)習(xí)中,函數(shù)是一等對(duì)象(first-class object),這就意味著你可以像使用字符串,整數(shù),或者任何其他對(duì)象一樣使用函數(shù)。
例如,你可以將函數(shù)賦值給變量:
>>> def square(n): ... return n * n; >>> square(4) 16 >>> alias = square >>> alias(4) 16
然而,一等函數(shù)的真正威力在于你可以把函數(shù)傳給其他函數(shù),或者從其他函數(shù)中返回函數(shù)。Python的內(nèi)置函數(shù)map利用了這種能力:給map傳個(gè)函數(shù)以及一個(gè)列表,它會(huì)依次以列表中每個(gè)元素為參數(shù)調(diào)用你傳給它的那個(gè)函數(shù),從而生成一個(gè)新的列表。如下所示的例子中應(yīng)用了上面的那個(gè)square函數(shù):
>>> number = [1, 2, 3, 4, 5] >>> map(square, numbers) [1, 4, 9, 16, 25]
如果一個(gè)函數(shù)接受其他函數(shù)作為參數(shù),以及/或者返回一個(gè)函數(shù),那么它就被稱為高階函數(shù) 。雖然map函數(shù)只是簡(jiǎn)單地使用了我們傳給它的函數(shù),而沒(méi)有改變這個(gè)函數(shù),但我們也可以使用高階函數(shù)去改變其他函數(shù)的行為。
例如,假設(shè)有這樣一個(gè)函數(shù),會(huì)被調(diào)用很多次,以致運(yùn)行代價(jià)非常昂貴:
>>> 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)
我們一般會(huì)保存計(jì)算過(guò)程中每次遞歸調(diào)用的結(jié)果,這樣,對(duì)于函數(shù)調(diào)用樹中經(jīng)常出現(xiàn)某個(gè)n,當(dāng)需要計(jì)算n對(duì)應(yīng)的結(jié)果時(shí),就不需要重復(fù)計(jì)算了。有多種方式可以做到這點(diǎn)。例如,我們可以將這些結(jié)果存在一個(gè)字典中,當(dāng)以某個(gè)值為參數(shù)調(diào)用fib函數(shù)時(shí),就先到這個(gè)字典去查一下其結(jié)果是否已經(jīng)計(jì)算出來(lái)了。
但這樣的話,每次我們想要調(diào)用fib函數(shù),都需要重復(fù)那段相同的字典檢查樣板式代碼。相反,如果讓fib函數(shù)自己在內(nèi)部負(fù)責(zé)存儲(chǔ)其結(jié)果,那么在其他代碼中調(diào)用fib,就非常方便,只要簡(jiǎn)單地調(diào)用它就行了。這樣一種技術(shù)被稱為memoization(注意沒(méi)有字母r的哦)。
我們可以把這種memoization代碼直接放入fib函數(shù),但是Python為我們提供了另外一種更加優(yōu)雅的選擇。因?yàn)榭梢跃帉懶薷钠渌瘮?shù)的函數(shù),那么我們可以編寫一個(gè)通用的memoization函數(shù),以一個(gè)函數(shù)作為參數(shù),并返回這個(gè)函數(shù)的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 函數(shù)以另一個(gè)函數(shù)作為參數(shù),函數(shù)體中創(chuàng)建了一個(gè)字典對(duì)象用來(lái)存儲(chǔ)函數(shù)調(diào)用的結(jié)果:鍵為被memoized包裝后的函數(shù)的參數(shù),值為以鍵為參數(shù)調(diào)用函數(shù)的返回值。 memoize 函數(shù)返回一個(gè)新的函數(shù),這個(gè)函數(shù)會(huì)首先檢查在 stored_results 字典中是否存在與當(dāng)前參數(shù)對(duì)應(yīng)的條目;如果有,對(duì)應(yīng)的存儲(chǔ)值會(huì)被返回;否則,就調(diào)用經(jīng)過(guò)包裝的函數(shù),存儲(chǔ)其返回值,并且返回給調(diào)用者。memoize返回的這種新函數(shù)常被稱為"包裝器"函數(shù),因?yàn)樗皇橇硗庖粋€(gè)真正起作用的函數(shù)外面的一個(gè)薄層。
很好,現(xiàn)在有了一個(gè)memoization函數(shù),我們可以把fib函數(shù)傳給它,從而得到一個(gè)經(jīng)過(guò)包裝的fib,這個(gè)版本的fib函數(shù)不需要重復(fù)以前那樣的繁重工作:
def fib(n): return n if n in [0, 1] else fib(n - 2) + fib(n - 1) fib = memoize(fib)
通過(guò)高階函數(shù)memoize,我們獲得了memoization帶來(lái)的好處,并且不需要對(duì)fib函數(shù)自己做出任何改變,以免夾雜著memoization的代碼而模糊了函數(shù)的實(shí)質(zhì)工作。但是,你也許注意到上面的代碼還算有點(diǎn)別扭,因?yàn)槲覀儽仨殞?遍fib。由于這種模式-傳遞一個(gè)函數(shù)給另一個(gè)函數(shù),然后將結(jié)果返回給與原來(lái)那個(gè)函數(shù)同名的函數(shù)變量-在使用包裝器函數(shù)的代碼中極為常見,Python為其提供了一種特殊的語(yǔ)法:裝飾器:
@memoize def fib(n): return n if n in [0, 1] else fib(n - 2) + fib(n -1)
這里,我們說(shuō)memoize函數(shù)裝飾了fib函數(shù)。需要注意的是這僅是一種語(yǔ)法上的簡(jiǎn)便寫法(譯注:就是我們常說(shuō)的"語(yǔ)法糖")。這段代碼與前面的代碼片段做的是同樣的事情:定義一個(gè)名為fib的函數(shù),把它傳給memoize函數(shù),將返回結(jié)果存為名為fib的函數(shù)變量。特殊的(看起來(lái)有點(diǎn)奇怪的)@語(yǔ)法只是減少了冗余。
你可以將多個(gè)裝飾器堆疊起來(lái)使用,它們會(huì)自底向上地逐個(gè)起作用。例如,假設(shè)我們還有另一個(gè)用來(lái)幫助調(diào)試的高階函數(shù):
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
下面的兩個(gè)代碼片段做的是同樣的事情:
@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并沒(méi)有限制你在@符號(hào)后只能寫一個(gè)函數(shù)名:你也可以調(diào)用一個(gè)函數(shù),從而能夠高效地傳遞參數(shù)給裝飾器。假設(shè)我們并不滿足于簡(jiǎn)單的memoization,還想將函數(shù)的結(jié)果存儲(chǔ)到memcached中。如果你已經(jīng)寫了一個(gè) memcached 裝飾器函數(shù),那么可以(例如)傳遞一個(gè)服務(wù)器地址給它:
@memcached('127.0.0.1:11211') def fib(n): return n if n in [0, 1] else fib(n - 2) + fib(n - 1)
非裝飾器語(yǔ)法的寫法會(huì)如下展開:
fib = memcached('127.0.0.1:11211')(fib)
Python配備有一些作為裝飾器使用的非常有用的函數(shù)。例如,Python有一個(gè) classmethod 函數(shù),可以創(chuàng)建大致類似于java的靜態(tài)方法:
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函數(shù)可以包含更多的信息,而不僅僅是代碼:它們也包含有用的幫助信息,比如函數(shù)名稱,文檔字符串:
>>> 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內(nèi)置函數(shù)help輸出的就是這些信息。但是,當(dāng)函數(shù)被包裝之后,我們看到就是包裝器函數(shù)的名稱和文檔字符串了:
>>> fib = memoized(fib) >>> fib.__name__ 'memoized' >>> fib.__doc__
那樣的信息并沒(méi)有什么用處。幸運(yùn)的是,Python包含一個(gè)名為 functools.wraps 的助手函數(shù),能夠把函數(shù)的幫助信息拷貝到其包裝器函數(shù):
import functools def memoize(fn): stored_results = {} @functools.wraps(fn) def memoized(*args): # (as before) return memoized
使用裝飾器幫助你編寫裝飾器會(huì)使很多事情令人非常滿意?,F(xiàn)在,如果使用更新過(guò)的memoize函數(shù)重試前面的代碼,我們將會(huì)看到得到保留的文檔:
>>> fib = memoized(fib) >>> fib.__name__ 'fib' >>> fib.__doc__ 'Recursively (i.e., dreadfully) calculate the nth Fibonacci number.'
到此,關(guān)于“Python函數(shù)的介紹以及裝飾器入門用法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。