Python之旅的第3*4天(函数闭包与装饰器)

与世无争的帅哥 提交于 2020-03-06 01:30:14

今天主要是讲了函数装饰器,真的是一个很强大的功能,总算整出来一点点贴近实际的东西,很开心,所以总结的有点晚了。

生成器的回顾:

# 上节课程内容的补充,关于生成器只能运行一次的问题
# def test():
#     for i in range(4):
#         yield i
#
# t = test()
# t1 = (i for i in t)    #此处只是产生生成器t1,不进行任何运算,并没有发生任何便利操作
# t2 = (i for i in t1)   #此处只是产生生成器t1,不进行任何运算,并没有发生任何便利操作
# print(list(t1))     #此时生成器t1才开始运算遍历,此时t1生成器已经完成一次遍历,生成:[0, 1, 2, 3]
# print(list(t2))     #此时t2才去调取t1生成器,但是此时生成器t1已经遍历过了,只有空,所以生成:[]
#以上内容尤其要注意的是,t1和t2在被赋值的时候不进行任何运算,知道list()发生时才开始进行调用运行。

装饰器的引入:

#装饰器的引入:
#装饰器的定义:本质就是函数,功能是为其他函数添加附加功能
#原则:1.不修改被装饰函数源代码;2.不修改被修饰函数的调用方式
#装饰器涉及的基础知识:装饰器 = 高阶函数 + 函数嵌套 + 闭包

#测试仅用高阶函数实现功能:
#功能需求,给函数增加测试运算时间的功能
#原函数代码:

# def test():
#     time.sleep(3)
#     print('我来自测试程序')

#方法一:直接修改原函数,增加计算函数运行时间功能
# def test():
#     start_time = time.time()
#     time.sleep(3)
#     print('我来自测试程序')
#     end_time = time.time()
#     print('测试函数运行时间是%s'%(end_time - start_time))
# test()
#无法满足函数装饰器的两个要求
#方法二:使用高阶函数,使高阶函数的参数为测试函数

# def test():
#     time.sleep(3)
#     print('我来自测试程序')
#
# def test_1(func):
#     start_time = time.time()
#     func()
#     end_time = time.time()
#     print('测试函数运行时间是%s'%(end_time - start_time))
#
# test_1(test)
#此时通过个高阶函数test_1提供函数参数,实现了在不改变原test代码的情况下增加功能
#但原函数test的调用方式发生了改变,依然不满足要求

#方法三:利用使高阶函数的返回值为函数,来实现函数调用方式不能改变的问题
# def test():
#     time.sleep(3)
#     print('我来自测试程序')
# def test_1(func):
#     return func
# test = test_1(test)
# test()
#此时实现了函数调用方式不发生改变的问题
#但是功能未能发生改变,此时是否感觉将方法二和三合并即可实现功能
#下面来试一试吧
# def test():
#     time.sleep(3)
#     print('我来自测试程序')
#
# def test_1(func):
#     start_time = time.time()
#     func()
#     end_time = time.time()
#     print('测试函数运行时间是%s'%(end_time - start_time))
#     return func
#
# test = test_1(test)
# test()
#看上去已经十分完美了,但是在最后返回函数的时候发生了问题,因为返回来func
#结果原函数test()多运行了一次,多打印了依据'我来自测试程序'
#所以依旧不能满足要求,那么我们就继续向下走着看吧

#函数的嵌套,要区别于函数的递归
#函数的嵌套即函数内部定义了一个函数
# def grandfater(name):
#     print('from grandfater %s'%name)
#     def baba():
#         print('from baba%s'%name)
#         print(locals())
#         def sunzi():
#             name = 'alex'
#             print('from sunzi %s'%name)
#             print(locals())
#         sunzi()
#     baba()
#
# grandfater('xiaoyao')
#以上就是一个典型的函数嵌套
#闭包就体现在作用域中
#现在三个都有了,我自己先尝试一下如何实现刚才的那个功能吧
#
# def time_lis(func):
#     def wrapper():
#         start_time = time.time()
#         func()
#         end_time = time.time()
#         print('测试函数运行时间是%s'%(end_time - start_time))
#     return wrapper
#
# @time_lis
# def test():
#     time.sleep(3)
#     print('我来自测试程序')
# # test = time_lis(test)
#以上的time_lis便实现了在不修改test源代码、不修改调用方式的情况下增加了test函数的功能
#但是每次都用test = time_lis(test)太麻烦了
#python中向我们提供了可以简写的语法,就是
# @ time_lis()  == test = time_lis(test)
# 同时@time_lis一定要放在被修饰函数定义函数的下面,就是把@time_lis放在对应的def下面
# test()

#以上就是关于修饰器的初步实现
#但是上面的这段函数仍有缺陷:
#如果test函数是一个有返回值的函数,但是我们在运行的过程中test函数是在wrapper函数下运行的,此时wrapper函数并没有返回值,所以/
#我们如果要增加返回值也应该是在wrapper函数下增加返回值
#具体操作如下
# def time_lis(func):
#     def wrapper():
#         start_time = time.time()
#         res = func()      #在导入函数test运行的时候我们用res变量获取到test函数的返回值
#         end_time = time.time()
#         print('测试函数运行时间是%s'%(end_time - start_time))
#         return res        #在wrapper函数下返回res,即test函数的返回值
#     return wrapper
#
# @time_lis
# def test():
#     time.sleep(3)
#     print('我来自测试程序')
#     return 'test的计算结果'
# print(test())    #如果没有在wrapper函数下增加test的返回值,此句返回结果将是None
#现在才算是基本实现一个简单的修饰器
#一个有返回值的修饰器

#下面还得考虑我们通常在使用函数的时候都会传递参数,但是如何将参数传递个wrapper函数呢
#涉及到的知识点就是解压序列
#a,b,c = [1,2,3,]  等同于a = 1,b = 2,c = 3
#python中如果交换两个参数的值
# f1 = 1
# f2 = 2
# f1,f2 = f2 ,f1
# print(f1,f2)    #如此即可完成两个变量值的交换
#接着说传递参数的相关知识
# def time_lis(func):
#     def wrapper(*arg,**kwargs):      #我们在运行test函数的本质就是运行wrapper函数,所以wrapper需要接收所有有可能出现的函数序列,\
#                                      # 首先将test收到的函数转为元组和字典形式,随后再通过解压序列的方式传送到下面的func函数
#         start_time = time.time()
#         res = func(*arg,**kwargs)     #在导入函数test运行的时候我们用res变量获取到test函数的返回值
#         end_time = time.time()
#         print('测试函数运行时间是%s'%(end_time - start_time))
#         return res        #在wrapper函数下返回res,即test函数的返回值
#     return wrapper
#
# @time_lis
# def test(name,age):
#     time.sleep(3)
#     print('我来自测试程序')
#     return '我叫%s,年龄%s'%(name,age)
# @time_lis
# def test1(name,age,male):
#     time.sleep(3)
#     print('我来自测试函数1')
#     return '我叫%s,年龄%s,性别%s'%(name,age,male)
#
# print(test('alex',18))
# print(test1('alex',18,'male'))
#此时,修饰器的目的终于完成了

下面是实现购物主页的一个操作

#下面来实现在网上购物之前为相关的操作函数前面增加登录验证功能
# def onload(func):
#     def wrapper(*arg,**kwargs):
#         a = 1
#         while a < 4:
#             user_name = input('请输入用户名>>>')
#             user_mima = input('请输入密  码>>>')
#             if user_name == 'baba' and user_mima == 'wenzhi':
#                 break
#             else:
#                 print('密码错误,请重新输入')
#                 a +=1
#         if a < 4:
#             res = func(*arg, **kwargs)
#             return res
#         else:
#             return '多次密码输入错误,你的账号被锁定'
#     return wrapper
# @onload
# def test():
#     return ('欢迎进入个人订单')
# @onload
# def test():
#     return ('欢迎进入购物车')
# @onload
# def test():
#     return ('欢迎进入首页')
# print(test())
#按照以上方法,我们就给每一个功能模块前面增加了登录验证功能

#但是上面的功能并不完善,网站中的用户往往不止一个,同时在一次登录之后便不会再进行重新登录
#所以需要增加用户名称密码清单,和正确名称密码保存状态
user_list=[
    {'name':'alex','passwd':'123'},
    {'name':'zhoujielun','passwd':'123'},
    {'name':'zhaolei','passwd':'123'},
    {'name':'wenzhi','passwd':'123'},
]
current_dic={'username':None,'login':False}
#
# def onload(func):
#     def wrapper(*arg,**kwargs):
#         if current_dic['username'] and current_dic['login']:
#             res = func()
#             return res
#         else:
#             a = 1
#             while a < 4:
#                 name = input('请输入用户名>>>')
#                 password = input('请输入密  码>>>')
#                 for user in user_list:
#                     if name == user['name'] and password == user['passwd']:
#                         current_dic['username'] = user['name']
#                         current_dic['login'] = True
#                         res = func()
#                         return res
#                 else:
#                     print('密码错误,请重新输入')
#                 a += 1
#             if a == 4:
#                 return '多次密码输入错误,你的账号被锁定'
#             else:
#                 pass
#     return wrapper
# @onload
# def dingdan():
#     print('欢迎进入个人订单')
# @onload
# def gouwuche():
#     return ('欢迎进入购物车')
# @onload
# def zhuye():
#     return ('欢迎进入首页')
#
# diandan()
# print(zhuye())
# print(gouwuche())

#上面的操作其实还是存在漏洞,比如进入主页的时候好像不用输入密码,所以可以使用闭包的方式再嵌套一层
#增加传递选择登陆模式参数的外层
def choice_onload(how_to = 'none'):
    def onload(func):
        def wrapper(*arg,**kwargs):
            if how_to =='zhuye':
                print('直接进吧')
                res = func()
                return res
            else:
                if current_dic['username'] and current_dic['login']:
                    res = func()
                    return res
                else:
                    a = 1
                    while a < 4:
                        name = input('请输入用户名>>>')
                        password = input('请输入密  码>>>')
                        for user in user_list:
                            if name == user['name'] and password == user['passwd']:
                                current_dic['username'] = user['name']
                                current_dic['login'] = True
                                res = func()
                                return res
                        else:
                            print('密码错误,请重新输入')
                        a += 1
                    if a == 4:
                        return '多次密码输入错误,你的账号被锁定'
                    else:
                        pass
        return wrapper
    return onload
@choice_onload
def dingdan():
    print('欢迎进入个人订单')
@choice_onload
def gouwuche():
    return ('欢迎进入购物车')
@choice_onload('zhuye')
def zhuye():
    return ('欢迎进入首页')

print(zhuye())  #这个时候就可以实现直接进入,不用输入密码

最后的这个登录系统其实还是有很多需要进一步改进的地方,比如用户信息是储存在文件中的,是否在开始之前需要提取用户信息,这个是需要时间的,争取周六更新出来吧!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!