Last Updated on 2019年10月15日
Python中的閉包是一個比較模糊的概念,不太好理解,我最近的面試中也被問及,在一個單例模式的實現上,我用裝飾器實現單例,然后面試官就問到了我對閉包的理解,回答的不太清楚。
所以,回來后,好好的查資料理解了一下閉包的概念。下面我們就一起來試著理解這個概念,這在對Python的不斷學習中,也是一個避不開的概念。希望對大家有用。
閉包
首先,閉包并不只是一個python中的概念,在函數式編程語言中基本都有應用。
閉包是引用了自由變量的函數,這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外。
所以簡單的說,就是函數定義中引用了函數外定義的變量,并且該函數可以在其定義環境外被執行,相同的函數實例在執行中此外部定義的變量是共享的。然后此外部定義的變量又是外部函數的內部變量,外部函數返回此定義的內部函數,這樣就形成了一個閉包。
看下面示例:
def a():
var = []
def b(para):
var.append(para)
print(var)
return b
s = a()
s(1)
s(2)
# 輸出結果:
[1]
[1, 2]
[3]
[3, 4]
如上,可以看到,內部函數b應用了外部變量var,在函數b中修改外部變量var,并打印出來,然后外部函數返回此內部函數。
通過外部函數,生成內部函數實例s,運行s函數,會發現外部變量var的值是可以保存和共享的。但是我們再次生成新的函數實例s2,會發現s2中的var變量和s的是不相關的,此外部變量是跟函數實例相關的。
如此,基本可以得到:
閉包中的引用的自由變量只和具體的閉包有關聯,閉包的每個實例引用的自由變量互不干擾。
一個閉包實例對其自由變量的修改會被傳遞到下一次該閉包實例的調用。
而創建一個閉包,基本也要滿足以下幾點:
必須有內嵌函數
內嵌函數必須引用外部變量
外部函數必須返回內嵌函數
到此, 你是不是發現這個跟什么很相似,是的,就是裝飾器!
Python裝飾器就是閉包概念的一種體現。
這也就解釋了為什么面試官在看到我用裝飾器實現單例的時候,會問我閉包。因為這就是很典型的閉包的,看下面單例模式的裝飾器:
from functools import wraps
def Singleton(cls):
instance = {}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instance:
instance[cls] = cls(*args, **kwargs)
return instance[cls]
return wrapper
其中,外部變量instance就是被內部函數wrapper引用,外部函數最后返回內部函數,就是一個典型的閉包。每個被此裝飾器裝飾后的類,都會有一個獨有的instance變量來保存實例,因為閉包中每個實例引用的自由變量instance互不干擾,所以能實現單例模式。
對單例模式不熟悉的,請看:《Python 單例模式》
OK,大致閉包的概念就是如此,理解閉包主要是要理解其中的設計思想,這樣才能更好的使用閉包。
有任何問題,歡迎留言