• day18 装饰器(下)+迭代器+生成器


    一、有参装饰器

    1 前提

    上节课讲了无参装饰器的用法,模板如下:

    def outter(func):#参数写死,为了给语法糖传值
        def warpper(*args,**kwargs):#参数写死,为了给func传值
            res = func(*args,**kwargs)
            return res
        return warpper
    @outter
    def index():
        print("这是主页")
        return 1
    res = index()
    print(res)
    >>>这是主页
    >>>1
    

    从这段模板代码我们可以看出,装饰器的两个函数我们都无法为它增加新的参数传值

    所以,当我们的装饰器需要跟多参数的时候,就只能通过闭包函数为他传值

    2 如何使用有参装饰器

    下面通过一个需求来表现有参装饰器的用途:

    例:编写装饰器对多个函数添加认证功能,如果登录成功过,其他函数就不需要再次登录

    分析需求:我们要编写认证功能的话只要添加个简单的有判断的装饰器即可,但是要判断之前是否成功过,我们就需要用到全局变量,如果登录成功把全局变量修改即可。所以我们在装饰器内部就要再判断一下,这个全局变量的值是什么,这里就会出现矛盾

    我们要先判断全局变量,如果没有登录过,就修改他的值,这里违背了变量的先定义后引用的原则

    所以基于这个矛盾,我们需要对装饰器传值,而在上面模板中我们已经知道,装饰器的函数我们都无法为他传值,因为各有用途都写死了。所以我们只能用第二种传值方式:闭包

    is_login = True
    def auth(login):
        def outter(func):
            def warpper(*args,**kwargs):
                if login:
                    name = input("你的名字:")
                    pwd = input("你的密码:")
                    if name == "hz" and pwd == "123":
                        print("登录成功")
                        func(*args,**kwargs)
                        global is_login
                        is_login=False
                    else:
                        print("输入错误")
                else:
                    func(*args,**kwargs)
            return warpper
        return outter
    #语法糖的定义:被装饰函数 = @后面的东西(被装饰的函数)
    #index = auth(is_login)(index)
    #auth(is_login) == outter
    #outter(index) ==warpper
    @auth(is_login)
    def index():
        print("你好")
    index()
    @auth(is_login)
    def home():
        print("欢迎回家")
    home()
    

    3 有参装饰器模板

    def 有参装饰器(x,y,z):
        def outter(func):
            def wrapper(*args, **kwargs):
                res = func(*args, **kwargs)
                return res
            return wrapper
        return outter
    @有参装饰器(1,y=2,z=3)
    def 被装饰对象():
        pass
    

    4 修正装饰器

    我们的装饰器最终目的就是要把(原函数+装饰器)修改成和没有修改的函数一样(属性上)

    但是,我们之前的学习装饰器还没有完成最终目的

    @timer
    def home(name):
        '''
        home page function
        :param name: str
        :return: None
        '''
        time.sleep(5)
        print('Welcome to the home page',name)
    
    print(help(home))
    '''
    打印结果:
    
    Help on function wrapper in module __main__:
    
    wrapper(*args, **kwargs)
    
    None
    

    每个函数在定义出来后就有非常多的属性,名字,注释等等,我们如果一一修改就非常麻烦,这里可以用functools模块下提供的一个装饰器wraps专门用来帮我们实现这个:

    from functools import wraps
    def timer(func):
        @wraps(func)
        def wrapper(*args,**kwargs):
            start_time=time.time()
            res=func(*args,**kwargs)
            stop_time=time.time()
            print('run time is %s' %(stop_time-start_time))
            return res
        return wrapper
    

    二、迭代器

    1 什么是迭代器

    迭代:就是每一次重复都是基于上一次运行得到结果来进行循环,单纯的重复不是迭代

    器:指工具

    这里要区别两种类似的循环:

    i = 0
    while True:
        i+=1
        print(i)
    
    while True:
        print("i")
    

    这两种都叫循环,但是第一种实现的就是迭代的过程,第二种就是单纯的循环

    2 为什么要有迭代器

    迭代器就是取值的工具,涉及到把多个值循环取出的类型

    例:字符串,列表,字典,集合,元组,打开的文件

    而上述6种数据类型,又分为可索引取值和不可索引取值

    可索引取值:字符串,列表,元组

    l = [1,2,3,4,5,6]
    count = 0
    while count<len(l):
        print(l[count])
        count+=1
    

    明显从中可以看出,索引取值的局限性,于是就有了迭代器:不依赖索引取值的方式。

    3 如何用迭代器

    3.1 可迭代对象

    可迭代对象是什么,可以做一个比喻

    桌子上有三个东西,一个是活着的鸡,一个是烤箱,一个是烤鸡

    活鸡:不能吃的东西,但是可以通过烤箱变成可以吃的烤鸡

    烤箱:是一个把活鸡转换成烤鸡的工具

    烤鸡:可以吃的东西

    把上述比喻转化成程序思维

    可迭代对象:不能进行迭代循环,但是可以通过工具转化成迭代器

    “——iter——”方法:可以把可迭代对象转化成迭代器的工具

    迭代器:可以进行迭代循环

    这里就会有人有疑问,在编程中如何一下子判断这个对象是不是可迭代对象

    我们可以通过iter方法测试一下,有这个内置方法他就是可迭代对象

    s1=''
    s1.__iter__()
    
    l=[]
    l.__iter__()
    
    t=(1,)
    t.__iter__()
    
    d={'a':1}
    d.__iter__()
    
    set1={1,2,3}
    set1.__iter__()
    
    with open('a.txt',mode='w') as f:
        f.__iter__()
        pass
    

    通过这个测试,我们发现我们需要测试的6种数据类型都是可迭代对象

    那么,如何用可迭代对象进行迭代循环呢

    第一步:把可迭代对象转化成迭代器

    第二步:用next方法读取迭代器内的值

    d={"A":1,"B":2}
    d_i = d.__iter__()
    while True:
        print(d_i.__next__())
    >>>A
    >>>B
    >>>抛出异常StopIteration
    

    这里我们对抓取异常重新处理循环

    d={"A":1,"B":2}
    d_i = d.__iter__()
    while True:
        try:
            print(d_i.__next__())
        except StopIteration:
            break
    >>>A
    >>>B
    

    3.2 可迭代对象和迭代器对象详解

    可迭代对象(可以转换成迭代器的对象):内置有iter方法的对象

    ​ 可迭代对象.iter_():得到迭代器对象

    迭代器对象:内置有iter方法和next方法的对象

    ​ 迭代器对象._next _():得到迭代器的下一个值

    ​ 迭代器对象._next _():得到迭代器对象本身,就是和没调用一样

    可迭代对象:列表,字符串,字典,元组,集合,文件对象

    迭代器对象:文件对象

    4 for循环的工作原理

    我们刚刚已经通过while循环解决了不可索引数据类型的迭代循环,这和for循环的工作原理基本一致

    第一步:把我们需要循环的数据类型转换成迭代器对象

    第二步:通过next方法拿到一个返回值,把这个值给i

    第三步:循环步骤二,直到抛出StopIteration异常,for循环会捕捉异常并退出循环

    l = [1,2,3]
    for i in l :
        print(i)
    >>>1
    >>>2
    >>>3
    

    5 迭代器的优缺点

    优点:

    1. 为序列与非序列类型提供了一种统一的迭代取值方式
    2. 惰性计算,迭代器本身是一个数据列,可以只在需要的时候去调用next取得下一个值,在同一时间内,迭代器只存在一个值。所以迭代器的容量是无线大的,但是对于其他的容器类型,如列表,通常不会一次性存放很多个值,受内存大小限制。

    缺点:

    1. 除非用完整个迭代器,否则无法获取迭代器长度
    2. 只能取下一个值,不能控制他如何取值,如果要重新开始,就要重新定义一个迭代器对象

    三、生成器

    我们之前获得的迭代器都是通过可迭代对象获得的,那么如果自定义一个迭代器呢

    在函数内一旦存在yield关键字,调用函数并不会执行代码

    会返回一个生成器对象,生成器就是自定义的迭代器

    def func():
        print('第一次')
        yield 1
        print('第二次')
    g = func()#返回一个生成器对象
    #g.__iter__()
    #g.__next__()
    #生成器就是迭代器
    res = g.__next__()
    print(res)
    #通过next方法调用迭代器的下一个值,会执行第一个yield上面的代码,并将该yield的返回值返回
    >>>第一次
    >>>1
    

    这里可以优化一下写法

    iter(可迭代对象) == 可迭代对象.__iter__()
    next(可迭代对象) == 可迭代对象.__next__()
    

    模拟range,自定义一个迭代器

    def my_range(start,stop,step):
        while start<stop:
            yield start #生成器取下一个值时函数从这下面开始运行
            start += step
    res = my_range(0,1000,7)
    print(res.__next__())
    print(res.__next__())
    >>>0
    >>>7
    

    yield关键字不同于return,return是直接把函数结束,而yield是把函数在这里挂起,等待下一次调用,可以返回多个返回值

  • 相关阅读:
    值得收藏的十二条Jquery随身笔记
    都来中大奖啦~双色球随机算法!
    巧妙使用div+css模拟表格对角线
    介绍两个非常好用的Javascript内存泄漏检测工具
    JQuery模仿淘宝天猫魔盒抢购页面倒计时效果
    JQuery巧妙利用CSS操作打印样式
    boost编译随笔
    Dev-C++安装第三方库boost
    比特币源码分析--C++11和boost库的应用
    QT +go 开发 GUI程序
  • 原文地址:https://www.cnblogs.com/hz2lxt/p/12561168.html
Copyright © 2020-2023  润新知