巨蟒python全棧開發-第15天 裝飾器

一.今日內容總覽python

關於函數的裝飾器
1.裝飾器(重點,難點)(要求:反覆寫,代碼很少可是很繞)
開閉原則:(好比,菜單是拆散的,一點點搞的,用友拆散本身的功能,之後就不用開發了)
(1)對功能的擴展開放
(2)對代碼的修改是封閉的

通用裝飾器語法:
def wrapper(fn):
def inner(*args,**kwargs): #聚合
#在目標函數以前的操做
ret=fn(*args,**kwargs) #打散
在目標函數以後
return inner
@wrapper #等價於 func=wrapper(func)
def func():
pass
func()

NC(用友,腦殘)
2.同一個函數 被多個裝飾器裝飾

@wrapper1
@wrapper2
@wrapper3
def func():
pass
1 2 3 func 3 2 1
總結:就近原則,一層一層寫

3.帶參數的裝飾器(難受)(明白函數的調用就行)
def wrapper_out(參數):
def wrapper(fn):
def inner(*args,**kwargs): #聚合
#在目標函數以前
ret=fn(*args,**kwargs) #打散
#在目標函數以後
return ret
return inner
return wrapper
@wrapper_out(實參) #執行的時候,先執行函數調用,而後使用返回值和前面的@組合成裝飾器語法糖
def func():
pass

 

二.今日內容大綱app

1.開閉原則函數

2.裝飾器測試

3.帶參數的裝飾器優化

4.多個裝飾器裝飾同一個函數spa

5.裝飾器的應用設計

 

三.今日內容詳解代理

1.開閉原則日誌

開閉原則:
對功能的擴展開放;
對代碼的修改是封閉的.
def chi():
    print('吃東西')
    print('喝東西')
chi()
chi()
#顯然不是咱們想要的,對程序繼續優化

 

2.裝飾器code

(1)

女媧  造人 補天
熱插拔:有時須要,有時不須要,這纔是程序須要的
def zaoren():
    # print('澆水')    #此需求有的時候須要,有的時候不須要

    print('捏個泥人')
    print('吹口仙氣')
    print('就出來人了')
zaoren()
zaoren()
zaoren()
上面的寫法依然沒知足咱們的需求

(2)

#三年大旱,沒有水?
def zaoren():
    # print('澆水')    #此需求有的時候須要,有的時候不須要
    print('捏個泥人')
    print('吹口仙氣')
    print('就出來人了')
def water():
    print('澆水')
    zaoren()
zaoren()    #不能一直修改這兩行代碼
water()
#這時的設計就不符合開閉原則

 

(3)裝飾器的源頭

# # 裝飾器
def wrapper(fn): # fn接收的是一個函數
    def inner():
        print("澆水")
        fn() # 調用你傳遞進來的函數
        print("睡一覺")
    return inner
def zaoren():
    print("捏個泥人")
    print("吹口仙氣")
    print("你就出來了")
zaoren = wrapper(zaoren)
zaoren()
zaoren()

 

(4)lol & 消消樂

#lol
def lol():
    print('雙擊lol')
    print('選擇狂戰士')
    print('進草叢')
    print('崩山擊,十字斬')
#開心消消樂
def kxxxl():
    print('雙擊消除')
    print('進入下一關')
    print('升級')
    print('繼續玩')

#開掛
#關閉外掛
def wrapper(fn):
    def inner():
        print('開掛')
        fn()
        print('關閉外掛')
    return inner
#lol裝飾器
lol=wrapper(lol)
lol()
#
#kxxxl裝飾器
kxxxl=wrapper(kxxxl)
kxxxl()
'''
結果:
開掛
雙擊lol
選擇狂戰士
進草叢
崩山擊,十字斬
關閉外掛
開掛
雙擊消除
進入下一關
升級
繼續玩
關閉外掛
'''

 

(5)

裝飾器傳參

def play(username,password):
    print('雙擊lol')
    print('登陸',username,password)
    print('選擇狂戰士')
    print('進草叢')
    print('崩山擊,十字斬')
    return '十字斬刀者'
def xiaoxiaole(qq):
    print('登陸qq帳號')
    print('消消樂')

開掛
關閉外掛
在目標函數前和後插入一段新的代碼,不改變原來的代碼
方法一:(投機版)
def wrapper(fn):   #fn=play
    def inner(*args,**kwargs):#無敵傳參 聚合接收到的是元組('alex',123)
        print('開掛')
        fn(*args,**kwargs)
        print('關閉外掛')
        return '十字斬刀者'
    return inner
play=wrapper(play)      #play=inner   左邊的play表明inner
w=play('alex','123')
print(w)            #在這裏返回的是inner的return

xiaoxiaole=wrapper(xiaoxiaole)
xiaoxiaole(11010)
方法二:
def wrapper(fn):   #fn=play
    def inner(*args,**kwargs):#無敵傳參 聚合接收到的是元組('alex',123)
        print('開掛')
        s=fn(*args,**kwargs)      #這裏也有變化
        print('關閉外掛')
        return s            #這裏返回的接收的是內部函數的,若是play沒有返回值,返回None
    return inner
play=wrapper(play)      #play=inner   左邊的play表明inner
w=play('alex','123')
print(w)                  #在這裏返回的是play的return

 

(6)

通用裝飾器寫法:(寫10遍以上!!!)
#python裏面的動態代理
#存在的意義:在不破壞原有函數和原有函數調用的基礎上,給函數添加新的功能
#inner至關於代理

def wrapper(fn):        #fn是目標函數
    def inner(*args,**kwargs):      #爲了目標函數的傳參
        '''在執行目標函數以前'''
        ret=fn(*args,**kwargs)      #調用目標函數,ret是目標函數的返回值
        '''在執行目標函數以後的操做,能夠加if判斷等等'''
        return ret          #目標函數返回值返回,保證函數正常的結束
    return inner

@wrapper #target_func=wrapper(target_func)
def target_func():
    pass
#target_func=wrapper(target_func)   #此時上面的fn就是target_func
target_func()   #此時執行的是inner
# #注意:不要用打印當成返回值
 

3.帶參數的裝飾器

def wrapper_out(flag):              #裝飾器自己的參數
    def wrapper(fn):                #目標函數
        def inner(*args,**kwargs):  #目標函數執行須要的參數
            if flag==True:
                print('問問金老闆,最近行情怎麼樣啊?')
                ret=fn(*args,**kwargs) #在執行目標函數以前
                print('金老闆再騙我,恨你')
                return ret
            else:
                ret=fn(*args,**kwargs)  #在執行目標函數以前
                return ret
        return inner
    return wrapper
@wrapper_out(True) #先執行wrapper_out(True) 返回一個裝飾器wrapper  再和@拼接  @裝飾器
                    # wrapper_out(True)
                    # yue=wrapper(yue)      #左邊的yue返回值是inner,右邊的yue纔是真正的下面的yue
def yue():  #被wrapper裝飾
    print('走啊,約不?')
yue()                                       #執行yue,這裏的yue是指inner,

結果測試:

'''
False:
走啊,約不?

True:
問問金老闆,最近行情怎麼樣啊?
走啊,約不?
金老闆再騙我,恨你
'''

 

4.多個裝飾器裝飾同一個函數

def wrapper1(fn):
    def inner(*args, **kwargs):
        print("1111111")
        ret = fn(*args, **kwargs)
        print("2222222")
        return ret
    return inner

def wrapper2(fn):
    def inner(*args, **kwargs):
        print("3333333")
        ret = fn(*args, **kwargs)
        print("44444444")
        return ret
    return inner

def wrapper3(fn):
    def inner(*args, **kwargs):
        print("555555")
        ret = fn(*args, **kwargs)
        print("666666")
        return ret
    return inner
# 就近原則
@wrapper1
@wrapper2
@wrapper3
def func():
    print("我是可憐的func")
func()
#本身總結:把最終要執行的核心語句當成魚的大刺對稱吃魚原則
# 1 2 3  func 3 2 1

 

5.裝飾器的應用

menu = ("查看", "添加", "修改", "刪除", "退出")
flag = False # 沒登陸

def login():
    global flag
    username = input("請輸入用戶名:")
    password = input("請輸入密碼:")
    if username == "alex" and password == "123":
        flag = True
        print("登陸")
    else:
        flag = False
        print("用戶名密碼錯誤")

# 登陸驗證裝飾器函數
def login_verify(fn):
    def inner(*args, **kwargs):
        # 登陸校驗
        while 1:
            if flag == True:
                ret = fn(*args, **kwargs)
                return ret
            else:
                print('對不起, 您尚未登陸')
                login()
    return inner

def chakan():
    print("==============================查看")

@login_verify
def tianjia():
    print("============================添加")
@login_verify
def xiugai():
    print("=======================修改")
@login_verify
def shanchu():
    print("=========================刪除")

while 1:
    for i in range(len(menu)):
        print(i+1, menu[i])
    num = input("請輸入你要執行的菜單:")
    if num == "1":
        chakan()
    elif num == "2":
        tianjia()
    elif num == "3":
        xiugai()
    elif num == "4":
        shanchu()
    elif num == "5":
        print("程序退出中..........")
        exit()
    else:
        print("輸入有誤. 請從新選擇!")

做業:

1.整理裝飾器的造成過程, 背誦裝飾器的固定格式

#裝飾器通用寫法
#存在的意義:在不破壞原有函數和原有函數調用的基礎上,給函數添加新的功能
#inner至關於代理

def wrapper(fn):     #fn是目標函數
    def inner(*args,**kwargs):   #爲了目標函數的傳參
        '''在執行目標函數以前的操做'''
        ret=fn(*args,**kwargs) #調用目標函數,ret是目標函數的返回值
        '''在執行目標函數以後的操做,能夠加if判斷等等'''
        return ret      #目標函數返回值返回,保證函數正常的結束
    return inner
@wrapper #target_func=wrapper(target_func)
def target_func():
    # print('1')
    pass
#target_func=wrapper(target_func) #此時上面的fn就是target_func
target_func()       #此時執行的是inner
#注意:不要用打印當成返回值

2.編寫裝飾器, 在每次執行被裝飾函數以前打印」

每次執行被裝飾函數之都要先通過這裏, 這裏根據需求添加代碼」

def wrapper(fn):     #fn是目標函數
    def inner(*args,**kwargs):   #爲了目標函數的傳參
        '''在執行目標函數以前的操做'''
        ret=fn(*args,**kwargs) #調用目標函數,ret是目標函數的返回值

        return ret      #目標函數返回值返回,保證函數正常的結束
    return inner
@wrapper #target_func=wrapper(target_func)
def target_func():
    pass
#target_func=wrapper(target_func) #此時上面的fn就是target_func
target_func()       #此時執行的是inner
3.編寫裝飾器, 在每次執行被裝飾函數以前打印」
每次執行被裝飾函數之都要通過這裏, 這裏根據需求添加代碼」
def wrapper(fn):     #fn是目標函數
    def inner(*args,**kwargs):   #爲了目標函數的傳參

        ret=fn(*args,**kwargs) #調用目標函數,ret是目標函數的返回值
        '''在執行目標函數以後的操做,能夠加if判斷等等'''
        return ret      #目標函數返回值返回,保證函數正常的結束
    return inner
@wrapper #target_func=wrapper(target_func)
def target_func():
    pass
#target_func=wrapper(target_func) #此時上面的fn就是target_func
target_func()       #此時執行的是inner

4.編寫裝飾器, 在每次執行被裝飾函數以前讓用戶輸入用戶名, 

密碼, 給用戶三次機會, 登陸成功以後, 才能訪問該函數

 
def login():
    global flag
    count=1
#方法一:
    # while 1:
    #     username = input("請輸入用戶名:")
    #     password = input("請輸入密碼:")
    #                                      #3次失敗,直接離開整個程序
    #     if username == "alex" and password == "123":
    #         flag = True                             #重點:登錄成功以後,改變全局變量爲True
    #         print("登陸成功")                       #登錄成功,直接進入下一步
    #         break
    #     else:
    #         flag = False
    #         print("用戶名密碼錯誤")
    #     if count==3:                            #if放在下邊和上邊是不同的
    #         exit()
    #     count+=1
    # else:
    #     print('您已經輸入錯誤了3次')
#方法二:
    # while count<4:
    #     username = input("請輸入用戶名:")
    #     password = input("請輸入密碼:")
    #                                      #3次失敗,直接離開整個程序
    #     if username == "alex" and password == "123":
    #         flag = True                             #重點:登錄成功以後,改變全局變量爲True
    #         print("登陸成功")                       #登錄成功,直接進入下一步
    #         break
    #     else:
    #         flag = False
    #         print("用戶名密碼錯誤")
    #     # if count==3:                            #if放在下邊和上邊是不同的
    #     #     exit()
    #     count+=1
    #     print(count)
    # else:
    #     print('您已經輸入錯誤了3次')
    #     exit()
#方法三:
    # for i in range(3):
    #     username = input("請輸入用戶名:")
    #     password = input("請輸入密碼:")
    #                                      #3次失敗,直接離開整個程序
    #     if username == "alex" and password == "123":
    #         flag = True                             #重點:登錄成功以後,改變全局變量爲True
    #         print("登陸成功")                       #登錄成功,直接進入下一步
    #         break
    #     else:
    #         flag = False
    #         print("用戶名密碼錯誤")
    #     # if count==3:                            #if放在下邊和上邊是不同的
    #     #     exit()
    #     count+=1
    #     print(count)
    # else:
    #     print('您已經輸入錯誤了3次')
    #     exit()


flag = False # 沒登陸
def wrapper(fn):     #fn是目標函數
    def inner(*args, **kwargs):
        # 登陸校驗
        while 1:
            if flag == True:                        #問題:函數是怎麼走這塊的?
                ret = fn(*args, **kwargs)
                return ret
            else:
                print('你好,請您登錄!')
                login()
    return inner
@wrapper #target_func=wrapper(target_func)

def target_func():
    print('冬瓜你好')
#target_func=wrapper(target_func) #此時上面的fn就是target_func
target_func()       #此時執行的是inner
 
5.編寫裝飾器, 爲多個函數加上認證功能(用戶的帳戶密碼來源於文件, 
用戶有三次登陸的機會), 要求, 若是用戶登陸成功了, 後續就不須要再次登陸了.
def login():
    global flag
    count=1
#方法一:
    s=1
    while s:
        username = input("請輸入用戶名:")
        password = input("請輸入密碼:")
                                         #3次失敗,直接離開整個程序
        with open('info', mode='r', encoding='utf-8') as f:
            for i in f.readlines():
                t_user,t_pwd=i.split('|')
                if username == t_user and password == t_pwd:
                    flag = True                             #重點:登錄成功以後,改變全局變量爲True
                    print("登陸成功")                       #登錄成功,直接進入下一步
                    # break
                    s=0
                else:
                    flag = False
                    print("用戶名密碼錯誤")
                    count += 1                          #注意應該在這裏,把每次錯誤的數字+1

                if count == 4:  # if放在下邊和上邊是不同的
                    print('您已經輸入錯誤了3次')
                    exit()
    else:
        print('進入下一步')

flag = False # 沒登陸
def wrapper(fn):     #fn是目標函數
    def inner(*args, **kwargs):
        # 登陸校驗
        while 1:
            if flag == True:                        #問題:函數是怎麼走這塊的?
                ret = fn(*args, **kwargs)
                return ret
            else:
                print('你好,請您登錄!')
                login()
    return inner
@wrapper #target_func=wrapper(target_func)

def target_func():
    print('冬瓜你好')
#target_func=wrapper(target_func) #此時上面的fn就是target_func
target_func()       #此時執行的是inner
6.給每一個函數寫一個記錄日誌的功能. 功能要求: 每一次調用函數以前, 要將函數名稱, 事件節點記錄到log的日誌中. 所需模塊:import timeprint(time.strftime(「%Y-%m-%d %H:%M:%S」))
相關文章
相關標籤/搜索