今日主要內(nèi)容

          * 閉包
          * 裝飾器初識
          * 標(biāo)準(zhǔn)裝飾器
          一、閉包

          (一)什么是閉包

          *
          閉包:內(nèi)層函數(shù)調(diào)用外層函數(shù)的變量就是閉包(不能是全局變量)
          def func1(): a = 10 def func2(): print(a) # 內(nèi)層函數(shù)調(diào)用外層函數(shù)的變量,這就是一個閉包 func1()
          *
          檢測閉包的方法

          * 函數(shù)名.__closure__
          * 若返回對象地址就是一個閉包,返回None就不是一個閉包
          * 注意:.__closure__檢測的是函數(shù)名,判斷這個函數(shù)是否是閉包 def func1(): a = 10 def func2():
          print(a) # 調(diào)用外層變量a,是閉包 print(func2.__closure__) # 判斷func2是否是閉包 func1() 運行結(jié)果:
          (<cell at 0x00000230F21874F8: int object at 0x0000000063258190>,) def func1():
          a = 10 def func2(): a = 10 print(a) # 使用自身函數(shù)變量a,不是閉包 print(func2.__closure__)
          func1() 運行結(jié)果: None
          *
          如何在全局空間中調(diào)用內(nèi)部函數(shù)

          * 外層函數(shù)返回內(nèi)層函數(shù)的函數(shù)名(內(nèi)存地址),就可在全局空間中調(diào)用內(nèi)部函數(shù) def func1(): a = 10 def func2():
          print(a) return func2 # 返回內(nèi)層函數(shù)的函數(shù)名 f = func1() # 此時f就是func2 f() # 調(diào)用內(nèi)層函數(shù)func2
          print(f.__closure__) 運行結(jié)果: 10 (<cell at 0x00000280A87574F8: int object at
          0x0000000063258190>,)
          (二)閉包的作用

          * 保護(hù)數(shù)據(jù)安全
          *
          說白話一點,如果數(shù)據(jù)放在全局變量中(頂格寫代碼),數(shù)據(jù)的安全性很低,因為所有人都可以無意間修改它,想解決這個問題我們可以把數(shù)據(jù)放到函數(shù)中,只要不調(diào)用這個函數(shù),我的數(shù)據(jù)就是安全的。但是有個問題,每次執(zhí)行函數(shù)完,解釋器就會自動清空此函數(shù)開辟的局部空間中所有內(nèi)容(包括數(shù)據(jù)開辟的空間),所以每次調(diào)用完函數(shù),我的數(shù)據(jù)就沒了,此時就需要利用到了閉包。閉包可以在內(nèi)層函數(shù)調(diào)用外層函數(shù)中的變量,而且內(nèi)層函數(shù)如果調(diào)用了外層函數(shù)中的變量,那這個變量將不會消亡,將會常駐內(nèi)存。所以,我們只需要在全局空間中調(diào)用這個閉包的內(nèi)層函數(shù),就可以使用我的數(shù)據(jù)了,而且數(shù)據(jù)的安全性也提高了。
          *
          將變量常駐內(nèi)存,供后續(xù)代碼使用
          def outer(): lst = [1,2,3,4,5] def inner(): return lst return inner f =
          outer() f_lst = f() print(f_lst) 運行結(jié)果: [1,2,3,4,5]
          (三)閉包的應(yīng)用

          * 防止數(shù)據(jù)被誤修改
          * 裝飾器(與閉包格式相同)
          二、裝飾器初識

          (一)軟件開發(fā)的六大原則(了解)

          * 開閉原則(Open Close Principle)——裝飾器依據(jù)
          * 里氏代換原則(Liskov Substitution Principle)
          * 依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)
          * 接口隔離原則(Interface Segregation Principle)
          * 迪米特法則(最少知道原則)(Demeter Principle)
          * 合成復(fù)用原則(Composite Reuse Principle)
          (二)裝飾器依據(jù)——開閉原則

          * 開放封閉原則:
          * 對功能擴(kuò)展開放
          * 對源碼修改封閉
          (三)裝飾器引入

          *
          相信大多數(shù)人都玩LOL,我們模擬一次游戲過程:

          * 向平常一樣的流程,十連跪... def play_lol(): print("登陸游戲") print("開始排位...")
          print("游戲中...") print("失敗...") print("結(jié)束游戲") play_lol() 運行結(jié)果: 登陸游戲 開始排位...
          游戲中... Virtory 結(jié)束游戲
          * 受不了了,開一個外掛吧,此時我的函數(shù)需要擴(kuò)展,在前后添加開啟關(guān)閉外掛的功能 def play_lol(): print("開啟外掛!") #
          添加開啟外掛功能 print("登陸游戲") print("開始排位...") print("游戲中...") print("勝利?。?!")
          print("結(jié)束游戲") print("關(guān)閉外掛!") # 添加關(guān)閉外掛功能 play_lol() 運行結(jié)果: 開啟外掛! 登陸游戲 開始排位...
          游戲中... 勝利?。?! 結(jié)束游戲 關(guān)閉外掛!
          * 但此時,違背了開閉原則,對源代碼進(jìn)行了修改。想一個方法,對源代碼進(jìn)行前后包裝,不改變源碼 def play_lol(): print("登陸游戲")
          print("開始排位...") print("游戲中...") print("勝利?。。?quot;) print("結(jié)束游戲") def new_play(): #
          將上面代碼前后包裝成了一個新的函數(shù),沒有改變源碼 print("開啟外掛!") play_lol() print("關(guān)閉外掛!") new_play()
          運行結(jié)果: 開啟外掛! 登陸游戲 開始排位... 游戲中... 勝利?。?! 結(jié)束游戲 關(guān)閉外掛!
          * 功能實現(xiàn)了,而且還沒有改變源碼,但是有一個問題,我們之前訪問調(diào)用的是play_lol這個函數(shù),但此時我們訪問調(diào)用的是new_play()
          這個函數(shù),相當(dāng)于改變了調(diào)用,還是違背了開閉原則,沒有達(dá)到擴(kuò)展的效果,此時我們就需要對這段代碼稍作變化 def play_lol():
          print("登陸游戲") print("開始排位...") print("游戲中...") print("勝利!??!") print("結(jié)束游戲") def
          wrapper(fn): # 裝飾器雛形 def inner(): print("開啟外掛!") fn() print("關(guān)閉外掛!") return
          inner func = wrapper(play_lol) # 調(diào)用裝飾器函數(shù)將我基礎(chǔ)函數(shù)傳入進(jìn)去包裝 play_lol = func #
          返回的是包裝函數(shù),將包裝函數(shù)重命名成我原來的函數(shù) play_lol() # 調(diào)用此函數(shù) 運行結(jié)果: 開啟外掛! 登陸游戲 開始排位... 游戲中...
          勝利?。?! 結(jié)束游戲 關(guān)閉外掛!
          * 上述代碼就引出了裝飾器的雛形,刨析一下:
          * 裝飾器雛形:與閉包的格式相同,兩層函數(shù)構(gòu)成:
          * 內(nèi)層函數(shù)就是我的包裝函數(shù),將擴(kuò)展的功能和原函數(shù)包在一起組成一個函數(shù)
          * 外層函數(shù)的作用就是給內(nèi)層函數(shù)傳參用的,傳入的是我原函數(shù)的函數(shù)名,在內(nèi)層調(diào)用
          * 裝飾器的返回值 return inner:裝飾器的返回值是內(nèi)層函數(shù)的函數(shù)名,真正進(jìn)行包裝擴(kuò)展的是內(nèi)層函數(shù)
          *
          將返回值重命名成原函數(shù)名:將返回值重命名成原來的函數(shù)名,其實就是把真正作用的包裝函數(shù)重命名成原來的函數(shù)名,所以就解決了調(diào)用新函數(shù)的問題,真正遵循了開閉原則,再次調(diào)用原來的函數(shù)其實真正運行的是裝飾器內(nèi)部的inner函數(shù)
          def wrapper(fn): # 裝飾器雛形 def inner(): print("開啟外掛!") fn() print("關(guān)閉外掛!") return
          inner func = wrapper(play_lol) # 調(diào)用裝飾器函數(shù)將我基礎(chǔ)函數(shù)傳入進(jìn)去包裝 play_lol = func #
          返回的是包裝函數(shù),將包賺函數(shù)重命名成我原來的函數(shù) play_lol() # 調(diào)用此函數(shù)
          *
          利用語法糖裝飾

          * 使用裝飾器的兩行代碼可以轉(zhuǎn)換成語法糖 func = wrapper(play_lol) # 調(diào)用裝飾器函數(shù)將我基礎(chǔ)函數(shù)傳入進(jìn)去包裝 play_lol
          = func # 返回的是包裝函數(shù),將包賺函數(shù)重命名成我原來的函數(shù)
          * 語法糖 @wrapper # 語法糖 def play_lol(): print("登陸游戲") print("開始排位...")
          print("游戲中...") print("勝利?。?!") print("結(jié)束游戲")
          *
          此時,裝飾器的雛形就出來了

          *
          裝飾器雛形
          def wrapper(fn): def inner(): """擴(kuò)展功能""" fn() """擴(kuò)展功能""" return inner @wrapper
          def func(): pass func()
          *
          游戲模擬繼續(xù)進(jìn)行

          * 就算開掛,我們也得選完英雄,才能進(jìn)入游戲,所以我們給基礎(chǔ)函數(shù)傳個參數(shù),但是我們真正執(zhí)行的是裝飾器內(nèi)部的inner包裝函數(shù),所以也要給inner傳入?yún)?shù)
          def wrapper(fn): # 裝飾器雛形 def inner(hero): # 套到裝飾器中內(nèi)層包裝函數(shù)參數(shù) print("開啟外掛!")
          fn(hero) # 基礎(chǔ)函數(shù)參數(shù) print("關(guān)閉外掛!") return inner @wrapper def play_lol(hero): #
          基礎(chǔ)函數(shù)參數(shù) print("登陸游戲") print("開始排位...") print(f"選擇英雄:{hero}") print("游戲中...")
          print("勝利?。。?quot;) print("結(jié)束游戲") play_lol("蓋倫") 運行結(jié)果: 開啟外掛! 登陸游戲 開始排位... 選擇英雄:蓋倫 #
          傳入的參數(shù) 游戲中... 勝利!?。?結(jié)束游戲 關(guān)閉外掛!
          * 雖然開掛了,但是還是會遇到巨坑無敵坑的隊友,我們得把他記下來舉報他,所以要給基礎(chǔ)函數(shù)填寫返回值,同時給真正執(zhí)行的inner函數(shù)填寫返回值 def
          wrapper(fn): # 裝飾器雛形 def inner(hero): print("開啟外掛!") ret = fn(hero) #
          接收基礎(chǔ)函數(shù)的返回值 print("關(guān)閉外掛!") return ret # 返回包裝后的函數(shù)的返回值 return inner @wrapper def
          play_lol(hero): # 基礎(chǔ)函數(shù)參數(shù) print("登陸游戲") print("開始排位...") print(f"選擇英雄:{hero}")
          print("游戲中...") print("勝利?。?!") print("結(jié)束游戲") return "坑比隊友:xxx" # 基礎(chǔ)函數(shù)返回值
          print(play_lol("蓋倫")) 運行結(jié)果: 開啟外掛! 登陸游戲 開始排位... 選擇英雄:蓋倫 游戲中... 勝利?。?! 結(jié)束游戲 關(guān)閉外掛!
          坑比隊友:xxx # 返回值
          * 到此,我們裝飾器標(biāo)準(zhǔn)模式也就出來了
          *
          裝飾器標(biāo)準(zhǔn)模式(非常重要)
          def wrapper(fn): def inner(*args, **kwargs): """擴(kuò)展功能""" ret = fn(*args,
          **kwargs) """擴(kuò)展功能""" return ret return inner @wrapper def func(): pass func()

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                久操手机在线精品免费视频 | the蜜臀av入口 | 国产农村妇女精品久久 | 99精品一区二区三区 | 国产欧美在线观看视频 |