1、什麼是閉包python
閉包(closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即便已經離開了創造它的環境也不例外。另外一種說法認爲閉包是由函數和與其相關的引用環境組合而成的實體。以上兩種解釋都太抽象了。還有一種比較容易的理解方式:當一個函數內部嵌套另外一個函數,內部函數體用到了外部函數的局部變量,而且外部函數的返回結果是內部函數名(或者說是內部函數對象),這樣在執行外部函數時獲得的是內部函數對象,此時雖然外部函數已經結束,但接下來繼續執行內部函數時,內部函數仍能訪問到所引用的外部函數的那個局部變量,也就是說此時外部函數雖然執行完畢,但內存並無釋放,還需等待內部函數執行完畢後才釋放內存。這種現象就叫作閉包。數據庫
def outer(name): def inner(age): print("name:", name, "age:", age) # 內部函數inner調用了外部函數outer的變量name return inner # 結果返回的時inner對象 f = outer("lilei") # 執行outer函數獲得的是inner對象,至關於f = inner f(18) # 至關於執行inner(18) 結果: name: lilei age: 18
咱們能夠經過__closure__屬性來查看函數是否存在閉包,若是存在,則返回一個cell對象,而且經過cell對象能夠查出閉包中所包含的全部外部變量。若是函數不存在閉包,則返回None.後端
def outer(name): def inner(age): print("name:", name, "age:", age) # 內部函數inner調用了外部函數outer的變量name return inner # 結果返回的時inner對象 f = outer("lilei") # 執行outer函數獲得的是inner對象,至關於f = inner f(18) # 至關於執行inner(18) print(f.__closure__) print(f.__closure__[0].cell_contents) 結果: name: lilei age: 18 (<cell at 0x02E3EF10: str object at 0x02E4FAA0>,) lilei def outer(name,age): print("name:", name, "age:", age) outer("lilei",18) print(outer.__closure__) 結果: name: lilei age: 18 None
2、閉包的應用——裝飾器閉包
python裝飾器本質上就是一個函數,它可讓其餘函數在不須要作任何代碼變更的前提下增長額外的功能,裝飾器的返回值也是一個函數對象。裝飾器函數的外部函數傳入咱們要裝飾的函數名字,返回通過裝時候函數的名字,內層函數(閉包)負責修飾被修飾的函數。ide
一、實質:是一個函數;函數
二、參數:是你要裝飾的函數名(並不是函數調用);學習
三、返回:是裝飾完的函數名(也非函數調用);網站
四、做用:爲已經存在的對象添加額外的功能;spa
五、特色:不須要對對象作任何的代碼上的改動。code
說了這麼多,裝飾器到底有什麼做用呢?python裝飾器就是用於拓展原來的函數功能的一種函數,這個函數的特殊之處在於臺的返回值也是一個函數,使用python裝飾器的好處就是在不用更改原函數代碼的前提下給函數增長新的功能。通常而言咱們想要拓展原來的函數功能,最直接的辦法就是侵入代碼裏面修改,但這種方法有弊端,由於修改源代碼不能保證源代碼的其餘模塊功能的有效性。而且修改源代碼還違反了軟件開發中的一個原則」開放-封閉「原則,簡單來講,它規定已經實現功能的代碼不該該被修改,但能夠被擴展。即:封閉,已經實現的功能代碼塊不該該被修改 開放,對現有功能的擴展開放。
舉例:好比你是一家視頻網站的後端開發工程師,大家網站的python專區有如下三種學習板塊:
一、寫法一
account = { "is_authenticated": False, # 用戶登錄成功就把它改爲True "username": "lilei", # 數據庫中的用戶信息 "pass_word": "123" # 數據庫中的用戶密碼 } def login(): if account["is_authenticated"] == False: user_name = input("user:").strip() password = input("password:").strip() if user_name == account["username"] and password == account["pass_word"]: print("welcome login....") account["is_authenticated"] = True else: print("wrong username or password") if account["is_authenticated"] == True: print("已經過驗證...") def home(): print("python專區".center(30, "-")) def first_stage(): print("python初級階段學習視頻".center(30, "-")) def second_stage(): login() print("python中級階段學習視頻".center(30, "-")) def third_stage(): login() print("python高級階段學習視頻".center(30, "-")) home() first_stage() second_stage() third_stage()
這種寫法直接對源代碼進行了修改,違反了「開放-封閉」原則。
二、寫法二
account = { "is_authenticated": False, # 用戶登錄成功就把它改爲True "username": "lilei", # 數據庫中的用戶信息 "pass_word": "123" # 數據庫中的用戶密碼 } def login(func): if account["is_authenticated"] == False: user_name = input("user:").strip() password = input("password:").strip() if user_name == account["username"] and password == account["pass_word"]: print("welcome login....") account["is_authenticated"] = True else: print("wrong username or password") if account["is_authenticated"] == True: func() def home(): print("python專區".center(30, "-")) def first_stage(): print("python初級階段學習視頻".center(30, "-")) def second_stage(): print("python中級階段學習視頻".center(30, "-")) def third_stage(): print("python高級階段學習視頻".center(30, "-")) home() first_stage() login(second_stage) login(third_stage)
這種寫法,雖然沒有改變源代碼,但改變了函數的調用方式。
三、寫法三
account = { "is_authenticated": False, # 用戶登錄成功就把它改爲True "username": "lilei", # 數據庫中的用戶信息 "pass_word": "123" # 數據庫中的用戶密碼 } def login(func): def inner(): # 在內部再定義一層函數 if account["is_authenticated"] == False: user_name = input("user:").strip() password = input("password:").strip() if user_name == account["username"] and password == account["pass_word"]: print("welcome login....") account["is_authenticated"] = True else: print("wrong username or password") if account["is_authenticated"] == True: func() return inner # 返回內部函數對象 def home(): print("python專區".center(30, "-")) def first_stage(): print("python初級階段學習視頻".center(30, "-")) def second_stage(): print("python中級階段學習視頻".center(30, "-")) def third_stage(): print("python高級階段學習視頻".center(30, "-")) home() first_stage() second_stage = login(second_stage) # 至關於inner對象 third_stage = login(third_stage) # 至關於inner對象 second_stage() # 執行inner() third_stage() # 執行inner()
這種寫法就實現了既不改變源代碼也不改變調用方式,但實現了功能的拓展,可是沒法給second_stage() 或 third_stage()函數傳參數。
四、寫法四
account = { "is_authenticated": False, # 用戶登錄成功就把它改爲True "username": "lilei", # 數據庫中的用戶信息 "pass_word": "123" # 數據庫中的用戶密碼 } def login(func): def inner(*args, **kwargs): # 在內部再定義一層函數 if account["is_authenticated"] == False: user_name = input("user:").strip() password = input("password:").strip() if user_name == account["username"] and password == account["pass_word"]: print("welcome login....") account["is_authenticated"] = True else: print("wrong username or password") if account["is_authenticated"] == True: func(*args, **kwargs) return inner # 返回內部函數對象 def home(): print("python專區".center(30, "-")) def first_stage(): print("python初級階段學習視頻".center(30, "-")) def second_stage(): print("python中級階段學習視頻".center(30, "-")) def third_stage(vip_level): if vip_level > 3: print("python高級階段學習視頻".center(30, "-")) else: print("vip level is too lower") home() first_stage() second_stage = login(second_stage) # 至關於inner對象 third_stage = login(third_stage) # 至關於inner對象 second_stage() # 執行inner() third_stage(1) # 執行inner(1)
這是裝飾器的終極寫法,既能夠不改變源碼實現對原有功能的擴展,還能夠給函數傳遞參數。