• python 全栈开发,Day12(函数的有用信息,带参数的装饰器,多个装饰器装饰一个函数)


    函数的执行时,*打散。
    函数的定义时,*聚合。

    from functools import wraps
    def wrapper(f):  # f = func1
        @wraps(f)
        def inner(*args,**kwargs): #聚合
            #args (1,2,3)
            '''执行函数之前的相关操作'''
            ret = f(*args,**kwargs)  # 打散 1,2,3
            '''执行函数之后的相关操作'''
            return ret
        return inner
    
    @wrapper  # func1 = wrapper(func1)  func1 = inner
    def func1(*args): #args (1,2,3) 聚合
        print(666)
        return args
    print(func1(*[1,2,3]))  # inner(3,5) 打散
    

    执行输出:

    666
    (1, 2, 3)

    一、函数的有用信息

    1.函数名 使用__name__方法获取

    2.函数的解释  使用__doc___方法获取

    举个例子

    def func1():
        """
        此函数是完成登陆的功能,参数分别是...作用。
        :return: 返回值是登陆成功与否(True,False)
        """
        print(666)
        # print(func1.__name__)
        # print(func1.__doc__)
        return True
    func1()
    print(func1.__name__) #获取函数名
    print(func1.__doc__) #获取函数名注释说明
    

    执行输出:

    666
    func1

    此函数是完成登陆的功能,参数分别是...作用。
    :return: 返回值是登陆成功与否(True,False)

    这个有什么用呢?比如日志功能,需要打印出谁在什么时间,调用了什么函数,函数是干啥的,花费了多次时间,这个时候,就需要获取函数的有用信息了

    带装饰器的函数

    def wrapper(f):  # f = func1
        def inner(*args,**kwargs): #聚合
            #args (1,2,3)
            '''执行函数之前的相关操作'''
            ret = f(*args,**kwargs)  # 打散 1,2,3
            '''执行函数之后的相关操作'''
            return ret
        return inner
    
    @wrapper
    def func1():
        """
        此函数是完成登陆的功能,参数分别是...作用。
        :return: 返回值是登陆成功与否(True,False)
        """
        print(666)
        return True
    
    func1()
    print(func1.__name__)
    print(func1.__doc__)
    

    执行输出:

    666
    inner
    执行函数之前的相关操作

    咦?为什么输出了inner,我要的是func1啊。因为函数装饰之后,相当于执行了inner函数,所以输出inner

    为了解决这个问题,需要调用一个模块wraps

    wraps将 被修饰的函数(wrapped) 的一些属性值赋值给 修饰器函数(wrapper) ,最终让属性的显示更符合我们的直觉

    完整代码如下:

    from functools import wraps
    def wrapper(f):  # f = func1
        @wraps(f) #f是被装饰的函数
        def inner(*args,**kwargs): #聚合
            #args (1,2,3)
            '''执行函数之前的相关操作'''
            ret = f(*args,**kwargs)  # 打散 1,2,3
            '''执行函数之后的相关操作'''
            return ret
        return inner
    
    @wrapper
    def func1():
        """
        此函数是完成登陆的功能,参数分别是...作用。
        :return: 返回值是登陆成功与否(True,False)
        """
        print(666)
        return True
    
    func1()
    print(func1.__name__)
    print(func1.__doc__)
    

      执行输出:

    666
    func1

    此函数是完成登陆的功能,参数分别是...作用。
    :return: 返回值是登陆成功与否(True,False)

    二、带参数的装饰器

    import time
    def timmer(*args,**kwargs):
        def wrapper(f):
            print(args, kwargs) #接收第1步的值
            def inner(*args,**kwargs):
                if flag:
                    start_time = time.time()
                    ret = f(*args,**kwargs)
                    time.sleep(0.3)
                    end_time = time.time()
                    print('此函数的执行效率%f' % (end_time-start_time))
                else:
                    ret = f(*args, **kwargs)
                return ret
            return inner
        return wrapper
    
    flag = True
    @timmer(flag,2,3)  # 两步:1,timmer(flag,2,3) 相当于执行wrapper 2.@wrapper 装饰器 func1 = wrapper(func1)
    def func1(*args,**kwargs):
        return 666
    
    print(func1())
    

    执行输出:

    (True, 2, 3) {}
    此函数的执行效率0.300183

    666

    代码分析

    import time #1.加载模块
    def timmer(*args,**kwargs): #2.加载变量  5.接收参数True,2,3
        def wrapper(f): #6.加载变量  8.f = func1
            print(args, kwargs) #9.接收timmer函数的值True,2,3
            def inner(*args,**kwargs): #10.加载变量. 13.执行函数inner
                if flag: #14 flag = True
                    start_time = time.time() #15 获取当前时间
                    ret = f(*args,**kwargs) #16 执行func1
                    time.sleep(0.3) #19 等待0.3秒
                    end_time = time.time() #20 获取当前时间
                    print('此函数的执行效率%f' % (end_time-start_time)) #21 打印差值
                else:
                    ret = f(*args, **kwargs)
                return ret #22 返回给函数调用者func1()
            return inner #11 返回给函数调用者wrapper
        return wrapper #7.返回给函数调用timmer(flag,2,3)
    
    flag = True #3 加载变量
    @timmer(flag,2,3)  # 4.执行函数timmer(flag,2,3) 17.执行函数func1 两步:1,timmer(flag,2,3) 相当于执行wrapper 2.@wrapper 装饰器 func1 = wrapper(func1)
    def func1(*args,**kwargs):
        return 666 #18 返回给函数调用者f(*args,**kwargs)
    
    print(func1()) #12 执行函数
    

      

     

    假定现在有100个函数,都加上了装饰器,增加了显示函数执行时间的功能,现在需要去掉!

    怎能办?一行行代码去删除吗?太low了。

    这个时候,直接在装饰器函数加一个参数即可。

    import time
    flag = True
    def wrapper(f):
        def inner(*args,**kwargs):
            if flag:
                start_time = time.time()
                ret = f(*args,**kwargs)
                time.sleep(0.3)
                end_time = time.time()
                print('此函数的执行效率%f' % (end_time-start_time))
            else:
                ret = f(*args, **kwargs)
            return ret
        return inner
    
    @wrapper
    def func1(*args,**kwargs):
        print(args,kwargs)
        return 666
    print(func1())

    执行输出:

    此函数的执行效率0.300431
    666

    现在需要关闭显示执行时间

    直接将flag改成false

    import time
    flag = False
    def wrapper(f):
        def inner(*args,**kwargs):
            if flag:
                start_time = time.time()
                ret = f(*args,**kwargs)
                time.sleep(0.3)
                end_time = time.time()
                print('此函数的执行效率%f' % (end_time-start_time))
            else:
                ret = f(*args, **kwargs)
            return ret
        return inner
    
    @wrapper
    def func1(*args,**kwargs):
        print(args,kwargs)
        return 666
    print(func1())
    

    执行输出:

    () {}
    666

    这样,所有调用的地方,就全部关闭了,非常方便

    写装饰器,一般嵌套3层就可以了

    a = 5
    def func1():
        a += 1
        print(a)
    
    func1()
    

    执行报错

    这里函数对全局变量做了改变,是不允许操作的。
    函数内部可以引用全局变量,不能修改。如果要修改,必须要global一下

    a = 5
    def func1():
        global a
        a += 1
        print(a)
    
    func1()
    

    执行输出6

    三、多个装饰器,装饰一个函数

    def wrapper1(func):  # func ==  f函数名
        def inner1():
            print('wrapper1 ,before func')  # 2
            func()
            print('wrapper1 ,after func')  # 4
        return inner1
    
    def wrapper2(func):  # func == inner1
        def inner2():
            print('wrapper2 ,before func')  # 1
            func()
            print('wrapper2 ,after func')  # 5
        return inner2
    
    @wrapper2  #  f = wrapper2(f)  里面的f==inner1  外面的f == inner2
    @wrapper1  # f = wrapper1(f)   里面的f==函数名f  外面的f == inner1
    def f():  # 3
        print('in f')
    
    f()  # inner2()
    

    执行输出:

    wrapper2 ,before func
    wrapper1 ,before func
    in f
    wrapper1 ,after func
    wrapper2 ,after func

    哪个离函数近,哪个先计算
    最底下的先执行

    执行顺序如下图:

    多个装饰器,都是按照上图的顺序来的

     今日作业:

    1.写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组
    例如:[('红心',2),('草花',2), …('黑桃','A')]
    
    2.写函数,传入n个数,返回字典{'max':最大值,'min':最小值}
    例如:min_max(2,5,7,8,4)
    返回:{'max':8,'min':2}
    
    3.写函数,专门计算图形的面积
    其中嵌套函数,计算圆的面积,正方形的面积和长方形的面积
    调用函数area('圆形',圆半径)  返回圆的面积
    调用函数area('正方形',边长)  返回正方形的面积
    调用函数area('长方形',长,宽)  返回长方形的面积
    def area():
    	def 计算长方形面积():
    		pass
    	
    	def 计算正方形面积():
    		pass
    
    	def 计算圆形面积():
    		pass
    
    4.写函数,传入一个参数n,返回n的阶乘
    例如:cal(7) 
    计算7*6*5*4*3*2*1
    
    5、编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果(升级题)
    5.1.为题目3编写装饰器,实现缓存网页内容的功能:(升级题)
    具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
    6给每个函数写一个记录日志的功能,
    功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。
    所需模块:
    import time
    struct_time = time.localtime()
    print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))
    
    
    7、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    
    8,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。这个作业之上进行升级操作:
    	设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。
    	设置四个函数,分别代表 京东首页,京东超市,淘宝首页,淘宝超市。
    	循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
    	供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
    相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。
    

    答案:

    1.写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组

    例如:[('红心',2),('草花',2), …('黑桃','A')]

    1.1准备基础数据

    #颜色
    colour = ['黑桃♠','红心♥','梅花♣','方块♦']
    #牌面的值
    card = list(range(2,11))+['A','J','Q','K']
    

    1.2使用for循环遍历

    #颜色
    colour = ['黑桃♠','红心♥','梅花♣','方块♦']
    #牌面的值
    card = list(range(2,11))+['A','J','Q','K']
    
    for i in card:
        for j in colour:
            print((j,i))
    

    执行输出:

    ('黑桃♠', 2)
    ('红心♥', 2)
    ('梅花♣', 2)

    ...

    1.3 封装成函数

    #颜色
    colour = ['黑桃♠','红心♥','梅花♣','方块♦']
    #牌面的值
    card = list(range(2,11))+['A','J','Q','K']
    
    def poker(*args,**kwargs):
        show_card = []
        for i in kwargs['card']:
            for j in kwargs['colour']:
                show_card.append((j, i))
        return show_card
    
    print(poker(colour=colour,card=card))
    

    执行输出:

    [('黑桃♠', 2), ('红心♥', 2), ('梅花♣', 2),...]

    老师的代码:

    def func(li):
        l = []
        for i in li:
            #用1~13表示13张牌
            for j in range(1,14):
                l.append((i,j))
        return l
    print(func(['草花', '黑桃', '红桃', '方片']))
    

    思考题,在上述代码的基础上修改一下

    A用1表示,2~10表示数字牌,JQK分别表示11,12,13

    代码如下:

    def func(li):
        l = []
        for i in li:
            #A用1表示,2~10表示数字牌,JQK分别表示11,12,13
            for j in range(1,14):
                if j == 1:
                    j = 'A'
                elif j == 11:
                    j = 'J'
                elif j == 12:
                    j = 'Q'
                elif j == 13:
                    j = 'K'
                l.append((i,j))
        return l
    print(func(['草花', '黑桃', '红桃', '方片']))
    

      

    2.写函数,传入n个数,返回字典{'max':最大值,'min':最小值}
    例如:min_max(2,5,7,8,4)
    返回:{'max':8,'min':2}

    2.1使用内置函数,可以得出最大和最小值

    a = (1,2,3)
    b = {'k1':1,'k2':2}
    print(max(a))
    print(min(b.values()))

    执行输出:

    3
    1

    2.2封装成函数

    def min_max(*args,**kwargs):
        dic = {'max':None,'min':None}
        number = []
        #循环位置变量
        for i in args:
            for j in i:
                number.append(j)
        # 循环关键字变量
        for k in kwargs.values():
            number.append(k)
        #最大值和最小值
        dic['max'] = max(number)
        dic['min'] = min(number)
        
        return dic
    
    print(min_max([2,6,7,8,3,7,678,3,432,6547],a=1))
    

    执行输出:

    {'min': 1, 'max': 6547}

    老师的代码:

    def func2(*args):
        return {'max':max(args), 'min':min(args)}
    
    print(func2(1,2,3,4,5))

    3.写函数,专门计算图形的面积
    其中嵌套函数,计算圆的面积,正方形的面积和长方形的面积
    调用函数area('圆形',圆半径) 返回圆的面积
    调用函数area('正方形',边长) 返回正方形的面积
    调用函数area('长方形',长,宽) 返回长方形的面积
    def area():
    def 计算长方形面积():
    pass

    def 计算正方形面积():
    pass

    def 计算圆形面积():
    pass

    先找出公式


    长方形面积公式
    S = ab
    公式描述:公式中a,b分别为长方形的长和宽,S为长方形的面积。

    正方形面积公式
    S = a²
    公式描述:公式中a为正方形边长,S为正方形面积。

    圆的面积公式
    S = πr²
    公式描述:公式中r为圆的半径,π用3.14表示

    写函数雏形

    def area(*args,**kwargs):
        #计算长方形面积
        def rectangle(*args,**kwargs):
            pass
    
        #计算正方形面积
        def square(*args,**kwargs):
            pass
    
        #计算圆形面积
        def circular(*args,**kwargs):
            pass
    
        print(args)
    
    ret = area('长方形', '长','宽')
    print(ret)
    

    执行输出:

    ('长方形', '长', '宽')
    None

    填补函数

    def area(*args,**kwargs):
        #计算长方形面积
        def rectangle(*args,**kwargs):
            #print(args)
            return args[0] * args[1]
    
        #计算正方形面积
        def square(*args,**kwargs):
            return args[0] ** 2
    
        #计算圆形面积
        def circular(*args,**kwargs):
            return 3.14 * (args[0] ** 2)
        
        #判断参数
        if args[0].strip() == '长方形':
            return rectangle(args[1],args[2])
        elif args[0].strip() == '正方形':
            return square(args[1])
        elif args[0].strip() == '圆形':
            return circular(args[1])
        else:
            return '参数不正确!'
    
    ret1 = area('长方形',3,4)
    ret2 = area('正方形',5)
    ret3 = area('圆形',6)
    print(ret1)
    print(ret2)
    print(ret3)
    

    执行输出:

    12
    25
    113.04

    老师的代码:

    def area(*args):
        #判断参数
        if args[0] == '长方形':
            def 计算长方形面积():
                s = args[1]*args[2]
                return s
            return 计算长方形面积()
        elif args[0] == '正方形':
            def 计算正方形面积():
                s = args[1] ** 2
                return s
            return 计算正方形面积()
        elif args[0] == '圆形':
            def 计算圆形面积():
                s = 3.14 * (args[1] ** 2)
                return s
            return 计算圆形面积()
    
    print(area('长方形',2,3))
    print(area('正方形',5))
    print(area('圆形',6))
    

      

    4.写函数,传入一个参数n,返回n的阶乘
    例如:cal(7)
    计算7*6*5*4*3*2*1

    4.1先用range输出倒序的数字

    range(开始,结束,步长)

    默认步长为1,如果为-1,表示倒序

    for i in range(7,0,-1):
        print(i)
    

      执行输出:

    7
    6
    5
    4
    3
    2
    1

    4.2封装函数

    def cal(*args,**kwargs):
        ret = 1
        for i in range(args[0],0,-1):
            ret *= i
        return ret
    
    print(cal(7))
    

    执行输出:

    5040

    老师的代码:

    def func3(n):
        count = 1
        for i in range(n,0,-1):
            count = count * i
        return count
    print(func3(7))
    

      

    5、编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果(升级题)
    5.1.为题目3编写装饰器,实现缓存网页内容的功能:(升级题)
    具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中

    import urllib.request
    import os
    import time
    def download_index(*args, **kwargs):
        flag = False
        def inner():
            if os.path.isfile('download.txt') == flag:
                for i in args:
                    url = 'https://' + str(i)
                    response = urllib.request.urlopen(url).read().decode('utf-8')
                    time.sleep(1)
                    with open('download.txt', encoding='utf-8', mode='w') as f2:
                        f2.write(response)
                    return response
            else:
                with open('download.txt', encoding='utf-8', mode='r') as f3:
                    #print(inner.__closure__)
                    return f3.read()
        return inner
    print(download_index('www.baidu.com')())
    

      执行输出:

    <html>

    <head>

    <script>

    ...

    </html>

    6.给每个函数写一个记录日志的功能,
    功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。
    所需模块:
    import time
    struct_time = time.localtime()
    print(time.strftime("%Y-%m-%d %H:%M:%S",struct_time))

    6.1准备示例函数

    import time
    def func1():
        """
        此函数是测试的
        :return:
        """
        print(666)
        time.sleep(0.3)
        return True
    
    func1()
    print(func1.__name__)
    print(func1.__doc__)
    

      执行输出:

    666
    func1

    此函数是测试的
    :return:

    准备装饰器模板

    def wrapper(f):
        def inner(*args,**kwargs):
            '''被装饰函数之前'''
            ret = f(*args,**kwargs)
            '''被装饰函数之后'''
            return ret
        return inner
    

      补充装饰器,完整代码如下:

    import time
    
    def wrapper(f):
        def inner(*args,**kwargs):
            '''被装饰函数之前'''
            ret = f(*args,**kwargs)
            '''被装饰函数之后'''
            #标准时间
            struct_time = time.localtime()
            standard_time = time.strftime("%Y-%m-%d %H:%M:%S",struct_time)
            print('函数名称:{} 时间节点:{}
    '.format(f.__name__,standard_time))
            return ret
        return inner
    
    @wrapper
    def func1():
        """
        此函数是测试的
        :return:
        """
        print(666)
        time.sleep(0.3)
        return True
    
    func1()
    

    执行输出:

    666
    函数名称:func1 时间节点:2018-04-02 16:25:05

    增加写入日志功能

    import time
    
    def wrapper(f):
        def inner(*args,**kwargs):
            '''被装饰函数之前'''
            ret = f(*args,**kwargs)
            '''被装饰函数之后'''
            #标准时间
            struct_time = time.localtime()
            standard_time = time.strftime("%Y-%m-%d %H:%M:%S",struct_time)
            #写入日志
            with open('function_log.txt',encoding='utf-8',mode='a+') as f1:
                f1.write('函数名称:{} 时间节点:{}
    '.format(f.__name__,standard_time))
            return ret
        return inner
    
    @wrapper
    def func1():
        """
        此函数是测试的
        :return:
        """
        print(666)
        time.sleep(0.3)
        return True
    
    func1()
    

    多执行几次程序,查看日志文件function_log.txt

     老师的代码:

    def wrapper(func):
        def inner(*args, **kwargs):
            struct_time = time.localtime()
            time_now = time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
            with open('log', encoding='utf-8', mode='a') as f1:
                f1.write('在时间是%s,执行了%s函数
    ' % (time_now, func.__name__))
            ret = func(*args, **kwargs)
            '''函数执行之后操作'''
            return ret
        return inner
    
    @wrapper
    def func1():
        time.sleep(1)
        print(555)
    @wrapper
    def func2():
        time.sleep(2)
        print(666)
    func1()
    func2()
    

    思考题

    执行func1和func2必须登录,才能执行。函数执行之后,记录日志

    时间: xx年xx月xx日  xx 执行了 xx 函数

    代码如下:

    import time
    dic = {
        'username':None,
        'status':False,
    }
    #错误次数
    i = 0
    
    def wrapper(func):
        def inner(*args, **kwargs):
            # 判断登录状态是否为True
            if dic['status']:
                # 执行被装饰行函数
                ret = func(*args, **kwargs)
                return ret
            else:
                # 这里需要修改全局变量,要global一下
                global i
                while i < 3:
                    username = input('请输入用户名:').strip()
                    password = input('请输入密码:').strip()
                    with open('register_msg', encoding='utf-8') as f1:
                        for j in f1:
                            j_li = j.strip().split()  # ['张三','123']
                            if username == j_li[0] and password == j_li[1]:
                                # 修改全局变量
                                dic['username'] = username
                                dic['status'] = True
                                struct_time = time.localtime()
                                time_now = time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
                                with open('log', encoding='utf-8', mode='a') as f1:
                                    f1.write('时间:%s %s执行了 %s函数
    ' % (time_now,username,func.__name__))
                                ret = func(*args, **kwargs)
                                return ret
                        else:
                            print('账号或者密码错误,请重新输入%s机会' % (2 - i))
                            i += 1
    
            '''函数执行之后操作'''
            #return ret
    
        return inner
    
    @wrapper
    def func1():
        time.sleep(0.4)
        print(555)
    
    @wrapper
    def func2():
        time.sleep(0.5)
        print(666)
    
    func1()
    func2()
    

    执行输出:

    查看日志

    7、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码

    7.1 准备函数雏形

    def check_login(func): #检查登陆的装饰器
        def inner(*args,**kwargs):
            '''函数被装饰之前'''
            ret = func(*args,**kwargs)
            '''函数被装饰之后'''
            return ret
        return inner
    
    def index():
        print("welcome to index page")
        
    @check_login
    def home(): #用户主页
        print("welcome to home page")
    
    @check_login
    def bbs(): #bbs页面
        print("welcome to bbs page")
    

    填补代码,完整代码如下:

    import os
    #文件名
    file_name = 'user_list.txt'
    #用户状态
    user_status = {'username':None,'status':False}
    
    def check_login(func): #检查登陆的装饰器
        def inner(*args,**kwargs):
            #print(user_status['status'])
            if user_status['status']:
                r = func(*args,**kwargs)
                return r
            else:
                print("33[41;1m请先登录!33[0m")
                #返回首页
                index()
    
        return inner
    
    #首页
    def index():
        print("welcome to index page")
        global user_status
        while True:
            username = input("username:").strip()
            password = input("password:").strip()
            # 判断文件是否存在
            if os.path.exists(file_name) == False:
                with open(file_name, encoding='utf-8', mode='w') as mk:
                    mk.write('xiao    123')
    
            with open(file_name, encoding='utf-8', mode='r') as f1:
                for i in f1:
                    # 去空格,以空格切割,转换为列表
                    li = i.strip().split()  # [张三,123]
                    # 判断用户名和密码是否匹配
                    if username == li[0] and password == li[1]:
                        result = True
                        # 当找到匹配时,跳出循环
                        break
                    else:
                        result = False
                # 当整个用户列表遍历完成之后,再判断result
                if result:
                    #更改全局变量
                    user_status['username'] = username
                    user_status['status'] = True
                    break
                else:
                    print('用户名和密码不正确,请重新输入')
    
    @check_login
    def home(): #用户主页
        print("welcome to home page")
    
    @check_login
    def bbs(): #bbs页面
        print("welcome to bbs page")
    
    index()
    home()
    bbs()
    

    执行输出:

     老师的代码:

    必须提前创建好文件register_msg,默认内容为'张三    123'

    #全局变量,用户状态
    dic = {
        'username':None,
        'status':False,
    }
    #错误次数
    i = 0
    
    def wrapper(func):
        def inner(*args, **kwargs):
            #判断登录状态是否为True
            if dic['status']:
                #执行被装饰行函数
                ret = func(*args, **kwargs)
                return ret
            else:
                #这里需要修改全局变量,要global一下
                global i
                while i < 3:
                    username = input('请输入用户名:').strip()
                    password = input('请输入密码:').strip()
                    with open('register_msg',encoding='utf-8') as f1:
                        for j in f1:
                            j_li = j.strip().split()  # ['张三','123']
                            if username == j_li[0] and password == j_li[1]:
                                #修改全局变量
                                dic['username'] = username
                                dic['status'] = True
                                ret = func(*args, **kwargs)
                                return ret
                        else:
                            print('账号或者密码错误,请重新输入%s机会' % (2-i))
                            i += 1
    
    
        return inner
    
    
    @wrapper
    def article():
        print('文章')
    
    @wrapper
    def diary():
        print('日记')
    
    @wrapper
    def comment():
        print('评论')
    
    @wrapper
    def file():
        print('文件')
    
    article()
    diary()
    comment()
    file()
    

      

    8,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。这个作业之上进行升级操作:
    设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。
    设置四个函数,分别代表 京东首页,京东超市,淘宝首页,淘宝超市。
    循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
    供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
    相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。

     不废话,直接贴代码

    # -*- coding: utf-8 -*-
    import os
    
    #文件名
    file_jd = 'jd.txt'
    file_taobao = 'taobao.txt'
    #用户状态
    user_status = {'username':None,'status':False,'user_type':None}
    
    def username_password(file_name,username,password):
        '''
        #判断用户名和密码是否匹配
        :param username: 用户名
        :param password: 密码
        :return: True 匹配成功 False 匹配失败
        '''
        if username == '' or password == '':
            return '用户名或者密码不能为空!'
        with open(file_name, encoding='utf-8', mode='r') as f3:
            for i in f3:
                # print(i)
                # 去空格,以空格切割,转换为列表
                li = i.strip().split()  # [张三,123]
                # 判断用户名和密码是否匹配
                if username == li[0] and password == li[1]:
                    result = True
                    #result = {'msg':True,'level':li[2]}
                    # 当找到匹配时,跳出循环
                    break
                else:
                    result = False
                    #result = {'msg': False, 'level': li[2]}
            # 当整个用户列表遍历完成之后,再return
            return result
    
    def check_login(func): #检查登陆的装饰器
        def inner(*args,**kwargs):
            if user_status['status']:
                r = func(*args,**kwargs)
                return r
            else:
                print("33[41;1m请先登录!33[0m")
                #返回首页
                login()
                func(*args, **kwargs)
        return inner
    
    
    def login(): #登录程序
        global user_status
        while True:
            print('请选择用户类型
    1.京东帐户
    2.淘宝账户
    ')
            user_type = input('请输入编号: ').strip()
            if user_type == '1' or user_type == '2':
                break
            else:
                print("33[41;1m输入错误,请重新输入!33[0m")
    
        while True:
            username = input('请输入用户名,或输入q返回主页:').strip()
            if username.upper() == 'Q':
                index()
            if username == '':
                print("33[41;1m用户名不能为空!33[0m")
            password = input('请输入密码:').strip()
            if password == '':
                print("33[41;1m密码不能为空!33[0m")
    
            if username != '' and password != '':
                #break
                # 判断文件是否存在
                if os.path.exists(file_jd) == False or os.path.exists(file_taobao) == False:
                    with open(file_jd, encoding='utf-8', mode='w') as f_jd:
                        f_jd.write('xiao    123')
                    with open(file_taobao, encoding='utf-8', mode='w') as f_taobao:
                        f_taobao.write('tao    123')
                if user_type == '1':
                    file_name = file_jd
                    user_type = '京东帐户'
                else:
                    file_name = file_taobao
                    user_type = '淘宝帐户'
    
                # 执行验证用户名和密码函数
                #print(file_name,username, password)
                result = username_password(file_name,username, password)
                if result:
                    print('登陆成功!
    ')
                    user_status['username'] = username
                    user_status['status'] = True
                    user_status['user_type'] = user_type
                    index()
                else:
                    print("33[41;1m用户名或密码错误!33[0m")
    
    #首页
    def index():
        menu = ['京东首页','京东超市','淘宝首页','淘宝超市','退出']
        #菜单函数名对照表
        menu_func = ['jd_index','jd_supermarket','taobao_index','taobao_supermarket','exit']
        print("꧁欢迎访问购物商城꧂".center(30,'❀'))
        status = '在线' if user_status['status'] == True else '未登录'
        info = '
    当前登陆用户:{} 状态: {} 用户类型:{}
    '.format(user_status['username'], status, user_status['user_type'])
        print(info.rjust(15))
        for i in range(len(menu)):
            print('{}	{}'.format(i + 1, menu[i]))
        print("".center(35, '☪'))
        while True:
            number = input("请输入序号:").strip()
            if number.isdigit():
                number = int(number)
                if number > 0 and number <= len(menu):
                    #执行菜单函数
                    eval(menu_func[number-1])()
            else:
                print('输入错误,请重新输入!')
    
    @check_login
    def jd_index(): #京东首页
        #print("欢迎访问 京东首页")
        print("33[32m欢迎访问 京东首页33[0m")
    
    @check_login
    def jd_supermarket(): #京东超市
        #print("欢迎访问 京东超市")
        print("33[32m欢迎访问 京东超市33[0m")
    
    @check_login
    def taobao_index(): #淘宝首页
        #print("欢迎访问 淘宝首页")
        print("33[32m欢迎访问 淘宝首页33[0m")
    
    @check_login
    def taobao_supermarket(): #淘宝超市
        #print("欢迎访问 淘宝超市")
        print("33[32m欢迎访问 淘宝超市33[0m")
    
    #执行函数
    if __name__ == '__main__':
        index()
    

    执行输出:

     老师的代码:

    dic = {
        'username':None,
        'status':False,
    }
    
    i = 0
    def login(flag):
        def wrapper(func):
            def inner(*args, **kwargs):
                #判断用户状态是否为True
                if dic['status']:
                    ret = func(*args, **kwargs)
                    return ret
                else:
                    global i
                    while i < 3:
                        username = input('请输入用户名(用%s账号):' % flag).strip()
                        password = input('请输入密码:').strip()
                        with open('user_pwd',encoding='utf-8') as f1:
                            #读取一行,因为文件只有一行内容
                            msg_dic = eval(f1.readline())
                            # {'微信': {'password': '123', 'username': '老男孩'}, 'qq': {'password': '123', 'username': '老男孩1'}}
                            if username == msg_dic[flag]['username'] and password == msg_dic[flag]['password']:
                                #修改全局变量,这里不用global,因为它是可变类型
                                dic['username'] = username
                                dic['status'] = True
                                #执行被装饰的函数
                                ret = func(*args, **kwargs)
                                return ret
                            else:
                                print('您输入的用户或者密码错误,请重新输入,还有%s次机会' % (2-i))
                                i += 1
    
            return inner
        return wrapper
    
    @login('微信')
    def taobao_home():
        print('淘宝首页')
    
    @login('微信')
    def taobao_shop():
        print('淘宝超市')
    
    @login('qq')
    def jingdong_home():
        print('京东首页')
    
    @login('qq')
    def jingdong_shop():
        print('京东超市')
    
    #菜单列表,建议使用此方式
    choice_dict = {
        #序号:函数名
        1: taobao_home,
        2: taobao_shop,
        3: jingdong_home,
        4: jingdong_shop,
        5: exit,
    }
    
    while True:
        print('1 淘宝首页
    2 淘宝超市
    3 京东首页
    4 京东超市
    5 退出')
        choice_num = input('请选择输入的序号:').strip()
        if choice_num.isdigit():
            choice_num = int(choice_num)
            #判断数字范围
            if 0 < choice_num <= len(choice_dict):
                #执行函数
                choice_dict[choice_num]()
            else:
                print('请输入范围内的序号')
        else:
            print('您输入的有非法字符,请重新输入')
    

    执行输出:

      

  • 相关阅读:
    Python简介
    开博啦
    关于ajax访问跨域问题
    关于PHP empty()函数的错误理解
    if判断 和&&
    元素和节点
    详解js和jquery里的this关键字
    document.body的一些用法以及js中的常见问题
    浏览器报错显示
    getAttribute()获取属性
  • 原文地址:https://www.cnblogs.com/xiao987334176/p/8692337.html
Copyright © 2020-2023  润新知