您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“如何使用閉包方法”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
什么是 Python 閉包?
首先,讓我使用一個(gè)簡(jiǎn)單的示例來說明什么是 Python 中的閉包??聪旅娴暮瘮?shù):
def outer(): x = 1 def inner(): print(f'x in outer function: {x}') return inner
在一個(gè)函數(shù)內(nèi)部定義另外一個(gè)函數(shù),并返回這個(gè)函數(shù),這種特性就是閉包。檢查 outer 函數(shù)的返回值,可以確認(rèn)這是一個(gè)函數(shù)。
>>> def outer(): ... x = 1 ... def inner(): ... print(f'x in outer function: {x}') ... return inner ... >>> outer <function outer at 0x7fb2ecdac9d0> >>> outer() <function outer.<locals>.inner at 0x7fb2ecdaca60> >>>
閉包這種特性能做什么呢?因?yàn)楹瘮?shù)返回的是一個(gè)函數(shù),我們就可以調(diào)用這個(gè)函數(shù),比如:
>>> outer()() x in outer function: 1 >>>
不過我們一般會(huì)這么使用閉包,這樣太丑陋了。你可能會(huì)好奇這個(gè)跟遞歸有什么關(guān)系?別著急,讓我們慢慢體會(huì)閉包的牛逼之處。
閉包內(nèi)的變量訪問
從前述的運(yùn)行結(jié)果來看,inner 函數(shù)可以訪問 outer 函數(shù)內(nèi)部定義的變量 x,但是卻無法修改它,下面的代碼運(yùn)行時(shí)會(huì)報(bào)錯(cuò):
>>> def outer(): ... x = 1 ... def inner(): ... print(f'x in outer function (before modifying): {x}') ... x += 1 ... print(f'x in outer function (after modifying): {x}') ... return inner ... >>> f = outer() >>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in inner UnboundLocalError: local variable 'x' referenced before assignment >>>
為了解決這個(gè)問題,我們可以加上 nonlocal 關(guān)鍵字,告訴 inner 函數(shù),這不是一個(gè)本地變量:
>>> def outer(): ... x = 1 ... def inner(): ... nonlocal x ... print(f'x in outer function (before modifying): {x}') ... x += 1 ... print(f'x in outer function (after modifying): {x}') ... return inner ... >>> >>> f = outer() >>> f() x in outer function (before modifying): 1 x in outer function (after modifying): 2 >>> f() x in outer function (before modifying): 2 x in outer function (after modifying): 3 >>> f() x in outer function (before modifying): 3 x in outer function (after modifying): 4 >>>
有沒有發(fā)現(xiàn),x 的值竟然被保存了下來,每次調(diào)用一下,就增加了 1,這就是閉包的妙處。
用閉包來替換遞歸
利用上述閉包會(huì)保留調(diào)用結(jié)果的特性,我們可以用這個(gè)來替換遞歸,比如利用閉包計(jì)算斐波那契數(shù)列:
def fib(): x1 = 0 x2 = 1 def get_next_number(): nonlocal x1, x2 x3 = x1 + x2 x1, x2 = x2, x3 return x3 return get_next_number
可以這樣調(diào)用來生產(chǎn)斐波那契數(shù)列:
>>> def fib(): ... x1 = 0 ... x2 = 1 ... def get_next_number(): ... nonlocal x1, x2 ... x3 = x1 + x2 ... x1, x2 = x2, x3 ... return x3 ... return get_next_number ... >>> fibonacci = fib() >>> for i in range(2, 21): ... num = fibonacci() ... print(f'The {i}th Fibonacci number is {num}') ... The 2th Fibonacci number is 1 The 3th Fibonacci number is 2 The 4th Fibonacci number is 3 The 5th Fibonacci number is 5 The 6th Fibonacci number is 8 The 7th Fibonacci number is 13 The 8th Fibonacci number is 21 The 9th Fibonacci number is 34 The 10th Fibonacci number is 55 The 11th Fibonacci number is 89 The 12th Fibonacci number is 144 The 13th Fibonacci number is 233 The 14th Fibonacci number is 377 The 15th Fibonacci number is 610 The 16th Fibonacci number is 987 The 17th Fibonacci number is 1597 The 18th Fibonacci number is 2584 The 19th Fibonacci number is 4181 The 20th Fibonacci number is 6765 >>>
而使用遞歸方法計(jì)算斐波那契數(shù)列的方法如下所示:
def fib_recursion(n:int) -> int: if n <= 1: return n return fib_recursion(n-1) + fib_recursion(n-2)
把之前的閉包版本封裝一下:
def fib(): x1 = 0 x2 = 1 def get_next_number(): nonlocal x1, x2 x3 = x1 + x2 x1, x2 = x2, x3 return x3 return get_next_number def fib_closure(n): f = fib() for i in range(2, n+1): num = f() return num
這樣使用 fib_closure(20) 就可以計(jì)算出結(jié)果:
In [4]: fib_closure(20) Out[4]: 6765 In [5]: fib_recursion(20) Out[5]: 6765 In [6]:
現(xiàn)在使用 IPython 來測(cè)試下這兩者的性能:
In [6]: %time fib_closure(20) CPU times: user 10 µs, sys: 1e+03 ns, total: 11 µs Wall time: 14.1 µs Out[6]: 6765 In [7]: %time fib_recursion(20) CPU times: user 2.76 ms, sys: 15 µs, total: 2.78 ms Wall time: 2.8 ms Out[7]: 6765
可以看出兩差相差近 1000 倍,這還只是計(jì)算到第 20 個(gè)數(shù)的情況下,如果計(jì)算到 100,那使用遞歸會(huì)計(jì)算很久甚至無法計(jì)算出來。
閉包的其他用處
Python 的閉包不僅僅用于替換遞歸,還有很多場(chǎng)景可以使用閉包。比如學(xué)生成績(jī)的分類函數(shù):
學(xué)生成績(jī)數(shù)據(jù):
students = { 'Alice': 98, 'Bob': 67, 'Chris': 85, 'David': 75, 'Ella': 54, 'Fiona': 35, 'Grace': 69 }
現(xiàn)在需要根據(jù)學(xué)生成績(jī)進(jìn)行分類,通常情況下我們會(huì)寫多個(gè)函數(shù)來進(jìn)行分類,而分類的標(biāo)準(zhǔn)又會(huì)經(jīng)常變化,這時(shí)候閉包就很方便了:
def make_student_classifier(lower_bound, upper_bound): def classify_student(exam_dict): return {k:v for (k,v) in exam_dict.items() if lower_bound <= v < upper_bound} return classify_student grade_A = make_student_classifier(80, 100) grade_B = make_student_classifier(70, 80) grade_C = make_student_classifier(50, 70) grade_D = make_student_classifier(0, 50)
如果分類標(biāo)準(zhǔn)變化,直接個(gè)性函數(shù)的參數(shù)即可,主要代碼邏輯不變,如果想查找成績(jī)分類為 A 的學(xué)生,只需要調(diào)用 grade_A(students) 即可:
In [13]: grade_A(students) Out[13]: {'Alice': 98, 'Chris': 85}
閉包使用上述分類函數(shù)很容易修改且更加易讀。
“如何使用閉包方法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(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)容。