巨蟒python全栈开发-第15天 装饰器

一.今日内容总览

关于函数的装饰器
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

二.今日内容大纲

1.开闭原则

2.装饰器

3.带参数的装饰器

4.多个装饰器装饰同一个函数

5.装饰器的应用

三.今日内容详解

1.开闭原则

开闭原则:
对功能的扩展开放;
对代码的修改是封闭的.
def chi():
    print('吃东西')
    print('喝东西')
chi()
chi()
#显然不是我们想要的,对程序继续优化

2.装饰器

(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 time
print(time.strftime(“%Y-%m-%d %H:%M:%S”))
 
原文地址:https://www.cnblogs.com/studybrother/p/10121641.html