Python函數(shù)之裝飾器

裝飾器:本質(zhì)是函數(shù),功能是裝飾其他函數(shù),就是為其他函數(shù)添加附加功能

編寫裝飾器的原則:

  • 不能修改被裝飾函數(shù)的源代碼
  • 不能修改被裝飾函數(shù)的調(diào)用方式

接下來模擬一個應(yīng)用場景從頭編寫一個裝飾器:
有一個視頻網(wǎng)站有四個模塊:

def home():
    print("---首頁----")

def domestic():
    print("----國內(nèi)專區(qū)----")

def america():
    print("----歐美專區(qū)----")

def japan():
    print("----日韓專區(qū)----")
 

視頻網(wǎng)站運(yùn)營一段一時間后積攢了些客戶,現(xiàn)在要為歐美和日韓模塊進(jìn)行收費(fèi),所以,調(diào)用該模塊時,需要判斷是否是付費(fèi)vip會員。
通過之前的函數(shù)基礎(chǔ)知識的學(xué)習(xí),輕易能想到的實(shí)現(xiàn)思路是:
單獨(dú)定義一個登陸函數(shù),在調(diào)用歐美和日韓模塊時調(diào)用:

user_status = False #用戶登錄了就把這個改成True
 
def login():
    _username = "LZ" #假裝這是DB里存的用戶信息
    _password = "abc123" #假裝這是DB里存的用戶信息
    global user_status
 
    if user_status == False:
        username = input("user:")
        password = input("pasword:")
 
        if username == _username and password == _password:
            print("welcome login....")
            user_status = True
        else:
            print("wrong username or password!")
    else:
        print("用戶已登錄,驗(yàn)證通過...")

def home():
    print("---首頁----")

def domestic():
    print("----國內(nèi)專區(qū)----")

def america():
   login() #執(zhí)行前加上驗(yàn)證
    print("----歐美專區(qū)----")

def japan():
   login() #執(zhí)行前加上驗(yàn)證
    print("----日韓專區(qū)----")

 #調(diào)用模塊
home()
domestic()
america()
japan()

這樣就能完成需求。但是??!這違反了軟件開發(fā)中的一個原則“開放-封閉”原則,簡單來說,它規(guī)定已經(jīng)實(shí)現(xiàn)的功能代碼不允許被修改,但可以被擴(kuò)展:

開放——封閉

  • 封閉:已實(shí)現(xiàn)的功能代碼塊
  • 開放:對擴(kuò)展開放

抱著不改變功能模塊源碼的想法我們會想到用高階函數(shù),把模塊函數(shù)當(dāng)參數(shù)傳入login()函數(shù)中:

user_status = False #用戶登錄了就把這個改成True
 
def login(func): #把要執(zhí)行的模塊從這里傳進(jìn)來
    _username = "LZ" #假裝這是DB里存的用戶信息
    _password = "abc123" #假裝這是DB里存的用戶信息
    global user_status
 
    if user_status == False:
        username = input("user:")
        password = input("pasword:")
 
        if username == _username and password == _password:
            print("welcome login....")
            user_status = True
        else:
            print("wrong username or password!")
 
    if user_status == True:
        func() # 看這里看這里,只要驗(yàn)證通過了,就調(diào)用相應(yīng)功能
#原功能模塊不改
#調(diào)用模塊
home()
domestic()
login(america)
login(japan)

這樣,不改變功能模塊源碼也完成了需求,但是改變了調(diào)用方式!試想:每個模塊不同的人負(fù)責(zé),這樣的話就得要求負(fù)責(zé)相應(yīng)模塊的人都得去改調(diào)用方式。這。。。很危險(xiǎn)啊。

現(xiàn)在在分析一下:之前函數(shù)基礎(chǔ)時講過函數(shù)也是變量,不改變函數(shù)調(diào)用方式的話,我們可以將login(america)賦值給america。但是有一個問題是當(dāng)執(zhí)行america = login(america)代碼時,就已經(jīng)把america執(zhí)行了啊!我們接下來要做的就是,當(dāng)america = login(america)代碼時,返回一個函數(shù),但是不執(zhí)行。等再調(diào)用america ()才執(zhí)行:

def login(func): #把要執(zhí)行的模塊從這里傳進(jìn)來
 
    def inner():#再定義一層函數(shù)
        _username = "alex" #假裝這是DB里存的用戶信息
        _password = "abc!23" #假裝這是DB里存的用戶信息
        global user_status
 
        if user_status == False:
            username = input("user:")
            password = input("pasword:")
 
            if username == _username and password == _password:
                print("welcome login....")
                user_status = True
            else:
                print("wrong username or password!")
 
        if user_status == True:
            func() # 看這里看這里,只要驗(yàn)證通過了,就調(diào)用相應(yīng)功能
 
    return inner #用戶調(diào)用login時,只會返回inner的內(nèi)存地址,下次再調(diào)用時加上()才會執(zhí)行inner函數(shù)

這樣當(dāng)調(diào)用時先america = login(america)你在這里相當(dāng)于把a(bǔ)merica這個函數(shù)替換了,然后在調(diào)用america ()。Python中提供語法在要被裝飾的函數(shù)前寫@login就相當(dāng)于america = login(america)。所以調(diào)用時:

 #調(diào)用模塊
home()
domestic()
@login
america()
@login
japan()

執(zhí)行完america = login(america)之后,america就相當(dāng)于調(diào)用的時inner.所以,如果原來的america如果有參數(shù)的話,在inner函數(shù)上添加參數(shù)即可:

def login(func): #把要執(zhí)行的模塊從這里傳進(jìn)來
 
    def inner(*args):#再定義一層函數(shù)
        _username = "alex" #假裝這是DB里存的用戶信息
        _password = "abc!23" #假裝這是DB里存的用戶信息
        global user_status
 
        if user_status == False:
            username = input("user:")
            password = input("pasword:")
 
            if username == _username and password == _password:
                print("welcome login....")
                user_status = True
            else:
                print("wrong username or password!")
 
        if user_status == True:
            func() # 看這里看這里,只要驗(yàn)證通過了,就調(diào)用相應(yīng)功能
 
    return inner #用戶調(diào)用login時,只會返回inner的內(nèi)存地址,下次再調(diào)用時加上()才會執(zhí)行inner函數(shù)

#調(diào)用

@login
america('jack')

寫到這,login就是一個裝飾器了。它是一個函數(shù),只是給原來的函數(shù)增加了驗(yàn)證功能。既沒有改變原函數(shù)代碼,也沒有改變調(diào)用方式!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,365評論 25 708
  • 有時候我們想為多個函數(shù),同意添加某一種功能,比如及時統(tǒng)計(jì),記錄日志,緩存運(yùn)算結(jié)果等等,而又不想改變函數(shù)代碼那就定義...
    ketchup閱讀 3,159評論 0 3
  • 如果我想念一個朋友,我想念的到底是什么。 是她或他的一個習(xí)慣,是她或他的一個品質(zhì)。 最近巧合地延續(xù)了一個多年前朋友...
    細(xì)雨薔薇閱讀 212評論 0 0
  • 前言碎語 前陣子上映的電影《喜歡你》,是一部以美食和愛情為主題的電影。在看影評時,有一段描述寫的特別好: “笑到累...
    一池YiCHi閱讀 478評論 0 1

友情鏈接更多精彩內(nèi)容