Python day14:閉包函數(shù)與裝飾器

一、閉包函數(shù)
閉包函數(shù)=函數(shù)嵌套定義+函數(shù)對象+名稱空間與作用域

1撬槽、閉:指的是該函數(shù)是定義在一個函數(shù)內(nèi)部的函數(shù)
2峡谊、包:值得是該函數(shù)訪問了一個來自于外層函數(shù)的變量

為函數(shù)體傳參的方法:

'''方案一:直接使用參數(shù)的形式傳遞'''
def wrapper(x):
    print(x)

wrapper(111)
wrapper(222)
'''方案二:把函數(shù)體想要的參數(shù)包給它(即使用閉包的概念)'''
def outter(x):
    x = 111

    def wrapper():  # wrapper = 閉包函數(shù)的內(nèi)存地址
        print(x)

    return wrapper  # 一定不要加括號
    return 閉包函數(shù)的內(nèi)存地址

f1 = outter(111)  # f = 閉包函數(shù)的內(nèi)存地址
f2 = outter(222)  # f = 閉包函數(shù)的內(nèi)存地址

乍一看會感覺使用閉包來傳參數(shù)非常的麻煩,我們之前使用函數(shù)姓赤,需要參數(shù)都是直接傳給他赡译,方便也快捷,但是在某些場景下我們定死了某個函數(shù)無法直接傳參數(shù)不铆,那就必須通過其他方式傳參數(shù)蝌焚,即閉包的方式,下面介紹的裝飾器就是閉包的使用

二誓斥、無參裝飾器
1只洒、什么是裝飾器
器:工具
裝飾:為被裝飾者添加額外的功能

2、為何要有裝飾器
軟件一旦上線運行之后劳坑,就應(yīng)該遵循開放封閉原則:
1红碑、開放指的是對拓展新功能開放
2、封閉指的是對修改源代碼封閉
這種情況下泡垃,我們在寫新功能時析珊,若需要用到新的參數(shù),就無法直接通過原函數(shù)來傳了蔑穴,必須通過其他方式來傳參忠寻,目前我們想到的方法,是兩種傳參的另一種的方式存和,閉包函數(shù)

定義裝飾器的目的:
定義裝飾器就是為了在遵循1和2的前提下來為其他函數(shù)添加新功能的

ps:
不修改被裝飾對象指的是定義與調(diào)用都不能修改
所以下述行為都違反了開放封閉原則:
①奕剃、修改被裝飾對象定義時的源代碼
②、修改被裝飾對象的調(diào)用方式

3捐腿、如何用裝飾器:

'''無參裝飾器基本格式'''
def inner(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return func
    return wrapper()

4纵朋、無參裝飾器的應(yīng)用場景以及構(gòu)建裝飾器步驟
需求:有一個index函數(shù),此時我們要在index函數(shù)基礎(chǔ)上添加一個計算運算時間的功能茄袖,因為是公司項目操软,我們不能修改原函數(shù)的調(diào)用方式,也不能修改源代碼宪祥。

一聂薪、被裝飾器對象index如下
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

index(1,2)

二家乘、為index添加計算運算時間功能
方案1問題:修改了源代碼
import time

def index(x,y):
    start = time.time()
    time.sleep(1)
    print("index---->",x,y)
    end = time.time()
    print(end-start)

index(1,2)

方案2問題:需要找到所有調(diào)用index的位置
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)


start = time.time()
index(1,2)
end = time.time()
print(end-start)

方案3問題:函數(shù)被寫死了只能算index的時間
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

def wrapper():
    start = time.time()
    index(1,2)
    end = time.time()
    print(end-start)

wrapper()

方案4:直接傳index進去,改變了index的調(diào)用方式藏澳,現(xiàn)在需要用wrapper來調(diào)用index仁锯,我們需要再想一種傳參的方式
import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

def wrapper(func):
    start = time.time()
    func(1,2)
    end = time.time()
    print(end-start)

wrapper(index)

方案5問題:此時利用閉包函數(shù),將index包給了wrapper翔悠,再重命名為index业崖,我們在調(diào)用index時其實是調(diào)用wrapper,但有出現(xiàn)了問題,\
原函數(shù)在調(diào)用的時候需要傳參數(shù)如index(1,2)蓄愁,而wrapper接收不了參數(shù)

import time

def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

def outter(func):
    # func = index()
    def wrapper():
        start = time.time()
        func(1,2)
        end = time.time()
        print(end-start)
    return wrapper

index = outter(index)
index()

方案6
import time
from functools import wraps

def timmer(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs)
        end = time.time()
        print(end-start)
        return res
    return wrapper

def login(func):
    def wrapper(*args,**kwargs):
        name = 'yang'
        pwd = '123'
        inp_name = input("請輸入您的用戶名:").strip()
        inp_pwd = input("請輸入您的密碼").strip()
        if inp_name == name and inp_pwd == pwd:
            print("登錄成功")
            res = func(*args,**kwargs)
            return res
    return wrapper

@timmer
@login
def index(x,y):
    time.sleep(1)
    print("index---->",x,y)

index(1,2)

三腻要、有參裝飾器
對于不再需要新參數(shù)的裝飾器,兩層就可以解決了涝登,但是當(dāng)我們的裝飾器需要新的參數(shù)雄家,如在登錄的時候,我們要判斷我們用戶名與密碼的來源胀滚,此時需要外部將來源傳進來趟济。而兩層的裝飾器中,第一層咽笼,是為了給原函數(shù)傳參數(shù)顷编,他的參數(shù)不能動,而第二層剑刑,因為裝飾器語法@的原因媳纬,也已經(jīng)定死了此處只能傳一個函數(shù)名,也不能傳參數(shù)施掏,所以我們需要構(gòu)造第三層來接受外部的參數(shù)钮惠,而在內(nèi)部的函數(shù)可以在不改動自己依然可以獲取到外層的函數(shù).

'''有參裝飾器模版'''
def outter2(x,y,z,a,b):
    def outter1(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            return res
        return wrapper
    return outter1

def outter(engine = 'file'):
    def deco2(func2):
        def wrapper2(*args,**kwargs):
            inp_name = input('username>>>: ').strip()
            inp_pwd = input('password>>>: ').strip()

            if engine == "file":
                print('基于file的認證')
                if inp_name == "egon" and inp_pwd == "123":
                    print('login successful')
                    res2=func2(*args,**kwargs)
                    return res2
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print('基于mysql的認證')
            elif engine == 'ldap':
                print('基于ldap的認證')
            else:
                print('未知的engine')
        return wrapper2
    return deco2

@outter(engine='mysql')  # @deco2 # index=deco2(index)
def index(x,y):
    print('index=>',x,y)

index(1,2)  # index=>wrapper

四、疊加多個裝飾器的運行步驟(記住結(jié)論既可)
2.1 加載順序:自下而上
2.2 執(zhí)行順序:自上而下運行內(nèi)層的wrapper函數(shù)
結(jié)論:

1七芭、無參裝飾器的模板
def outter(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return wrapper


2素挽、疊加多個裝飾器
**2.1 加載順序:自下而上**
**2.2 執(zhí)行順序:自上而下運行內(nèi)層的wrapper函數(shù)**

def deco1(func1):  # func1 = wrapper2的內(nèi)存地址
    def wrapper1(*args,**kwargs):
        print('wrapper1====>')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1

def deco2(func2):  # func2 = wrapper3的內(nèi)存地址
    def wrapper2(*args,**kwargs):
        print('wrapper2====>')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2

def deco3(func3):  # func3 = 最原始的那個被裝飾函數(shù)的內(nèi)存地址
    def wrapper3(*args,**kwargs):
        print('wrapper3====>')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3

        # index=wrapper1的內(nèi)存地址
@deco1  # deco1(wrapper2的內(nèi)存地址)=>wrapper1的內(nèi)存地址
@deco2  # deco2(wrapper3的內(nèi)存地址)=>wrapper2的內(nèi)存地址
@deco3  # deco3(最原始的那個被裝飾函數(shù)的內(nèi)存地址)=>wrapper3的內(nèi)存地址  
def index(x,y):
    print('index=>',x,y)

index(1,2)

"""
wrapper1====>'
wrapper2====>
wrapper3====>
index=>1,2
"""


3 案例
import time

def deco1(func1):
    def wrapper1(*args,**kwargs):
        start_time = time.time()
        res1=func1(*args,**kwargs)
        stop_time = time.time()
        print(stop_time - start_time)
        return res1
    return wrapper1


def deco2(func2):
    def wrapper2(*args,**kwargs):
        inp_name = input('username>>>: ').strip()
        inp_pwd = input('password>>>: ').strip()
        if inp_name == "egon" and inp_pwd == "123":
            print('login successful')
            res2=func2(*args,**kwargs)
            return res2
        else:
            print('username or password error')
    return wrapper2

@deco2
@deco1
def index(x,y):
    time.sleep(1)
    print('index=>',x,y)

index(1,2)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市狸驳,隨后出現(xiàn)的幾起案子预明,更是在濱河造成了極大的恐慌,老刑警劉巖耙箍,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撰糠,死亡現(xiàn)場離奇詭異,居然都是意外死亡辩昆,警方通過查閱死者的電腦和手機阅酪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人遮斥,你說我怎么就攤上這事∩却裕” “怎么了术吗?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帆精。 經(jīng)常有香客問我较屿,道長,這世上最難降的妖魔是什么卓练? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任隘蝎,我火速辦了婚禮,結(jié)果婚禮上襟企,老公的妹妹穿的比我還像新娘嘱么。我一直安慰自己,他們只是感情好顽悼,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布曼振。 她就那樣靜靜地躺著,像睡著了一般蔚龙。 火紅的嫁衣襯著肌膚如雪冰评。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天木羹,我揣著相機與錄音甲雅,去河邊找鬼。 笑死坑填,一個胖子當(dāng)著我的面吹牛抛人,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脐瑰,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼函匕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蚪黑?” 一聲冷哼從身側(cè)響起盅惜,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎忌穿,沒想到半個月后抒寂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡掠剑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年屈芜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡井佑,死狀恐怖属铁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情躬翁,我是刑警寧澤焦蘑,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站盒发,受9級特大地震影響例嘱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宁舰,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一拼卵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛮艰,春花似錦腋腮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仍律,卻和暖如春嘿悬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背水泉。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工善涨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人草则。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓钢拧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炕横。 傳聞我的和親對象是個殘疾皇子源内,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359