Python教程: 闭包及陷阱
Python語言是支持函數式編程的,我們可以在一個函數的函數體中定義另一個完整的函數,甚至返回這個函數.在函數內部定義的函數和外部定義的函數是相同的,唯一的區別就是在函數內部定義的函數是不能被外部訪問的.
1. 閉包的概念
閉包(Closure):內層函數引用了外層函數的變量(包括它的參數),然后返回內層函數的情況,這就是閉包.
在通過Python的語言介紹一下,一個閉包就是你調用了一個函數A,這個函數A返回了一個函數B給你。這個返回的函數B就叫做閉包。你在調用函數A的時候傳遞的參數就是自由變量。
2. 為什么使用閉包
基于上面的介紹,不知道讀者有沒有感覺這個東西和類有點相似,相似點在于他們都提供了對數據的封裝。不同的是閉包本身就是個方法。和類一樣,我們在編程時經常會把通用的東西抽象成類,(當然,還有對現實世界——業務的建模),以復用通用的功能。閉包也是一樣,當我們需要函數粒度的抽象時,閉包就是一個很好的選擇。
在這點上閉包可以被理解為一個只讀的對象,你可以給他傳遞一個屬性,但它只能提供給你一個執行的接口。因此在程序中我們經常需要這樣的一個函數對象——閉包,來幫我們完成一個通用的功能,比如后面會提到的——裝飾器
3. 使用閉包
第一種場景 ,在python中很重要也很常見的一個使用場景就是裝飾器,Python為裝飾器提供了一個很友好的“語法糖”——@,讓我們可以很方便的使用裝飾器,裝飾的原理不做過多闡述,簡言之你在一個函數func上加上@decorator_func, 就相當于decorator_func(func):
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' def decorator_func(func):def wrapper(*args, **kwargs):return func(*args, **kwargs)return wrapper@decorator_func def func(name):print('my name is', name)# 等價于 decorator_func(func)func("tracydzf")在裝飾器的這個例子中,閉包(wrapper)持有了外部的func這個參數,并且能夠接受外部傳過來的參數,接受過來的參數在原封不動的傳給func,并返回執行結果。
這是個簡單的例子,稍微復雜點可以有多個閉包,比如經常使用的那個LRUCache的裝飾器,裝飾器上可以接受參數@lru_cache(expire=500)這樣。實現起來就是兩個閉包的嵌套:
def lru_cache(expire=5):# 默認5s超時def func_wrapper(func):def inner(*args, **kwargs):# cache 處理 bala bala balareturn func(*args, **kwargs)return innerreturn func_wrapper@lru_cache(expire=10*60) def get(request, pk):# 省略具體代碼return response()閉包的陷阱
先看代碼
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' # 希望一次返回3個函數,分別計算1x1,2x2,3x3: def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fs f1, f2, f3 = count() print(f1(), f2(), f3()) 得到的結果是 9 9 9Python語言的特性所致.當count()函數返回三個函數時,這三個變量所引用的變量i的值已經變成了3,因為在返回的時候三個函數并沒有被調用,所以此時它們并沒有及時計算它們對應的i乘以i的值,等到三個函數都返回時,然后調用三個函數,此時i的值已經為3,計算i乘以i的值自然就都是9了.
所以在返回閉包的情況下,我們一定要注意的一點就是:
返回函數千萬不要引用任何一個循環變量,或者在之后會發生改變的變量.
當然對于這種情況我們還是有解決方法的.我們在內層函數f()內再定義一個內層函數g(),用這個函數的參數綁定循環變量的當前值,這樣的話,無論循環變量之后如何改變,每一次循環中的循環變量i的值就都保存在了第三層函數g()中,如此得到了我們期望的輸出結果.
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' def count():fs = []for i in range(1, 4):def f(j):def g():return j*jreturn gfs.append(f(i))return fsf1, f2, f3 = count()print(f1(), f2(), f3()) 程序的執行結果:1 4 9如果嫌這段代碼過于臃腫,可以考慮使用lambda表達式進行縮減.
def create_multipliers():return [lambda x, i=i : i * x for i in range(5)]for i in create_multipliers():print(i(2)) 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Python教程: 闭包及陷阱的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python之一行代码
- 下一篇: python教程:super()的用法