zoukankan      html  css  js  c++  java
  • 7.15 迭代器和生成器

    1.迭代器

      迭代就是一个更新换代的过程,每次迭代都必须基于上一次的结果。

      迭代器就是迭代取值的工具。举个例子:

    while True:
        print('循环输出')

      此代码会无限循环输出文字,是个死循环,并不是迭代,因为迭代是每次输出都与上一次输出有关,所以迭代应该是这样:

    list1=[1,2,3,4,5]
    n=0
    while n<len(list1):
        print(list1[n])
        n+=1
    #输出结果>>>1
    #2
    #3
    #4
    #5

      迭代也是有对象的,有的数据类型就不支持迭代

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

      其中,字典被迭代后获取的是字典中的key值。

      为什么要使用迭代:迭代提供了不依赖索引取值的方法。如字典,集合等无序的数据类型,是不可以通过循环取值的。

      2.可迭代对象。

      当一个对象的内置方法中,有__iter__方法的都叫做可迭代对象。

      __iter__执行后,就是获取该对象的迭代器对象。

    list1=[1,2,13,23,21]
    set1={12,2,43,414,41,4,2}
    str1="231"
    f=open('text1',mode='r',encoding='utf-8')
    print(list1.__iter__())
    print(set1.__iter__())
    print(str1.__iter__())
    print(f,f.__iter__())
    #输出结果>>><list_iterator object at 0x000002016F6BBBE0>
    #<set_iterator object at 0x000002016F6C1090>
    #<str_iterator object at 0x000002016F6BBBE0>
    #<_io.TextIOWrapper name='text1' mode='r' encoding='utf-8'> <_io.TextIOWrapper name='text1' mode='r' encoding='utf-8'>

      执行后获取的就是迭代器对象,对其进行数据类型转换也可以获得该列表。

      其中,f文件对象的__iter__(),获取后,和f原来的是一样的,所以,文件对象本身就是一个迭代器对象,迭代器对象的__iter__()还是它本身。

      判断一个对象是否是迭代器对象,需要满足两个条件:

      1。内部有__iter__(),方法。

      2.内部有__next__(),方法,

      所以可以得出,一个可迭代对象不一定是迭代器对象,而一个迭代器对象一定是个可迭代对象。

      __next__()所代表的就是对迭代器对象进行取值,当迭代器开始时需要进行初始化即把迭代器对象赋值给变量:

    d=[1,2,3]
    iter_d = d.__iter__()
    print(d.__iter__().__next__())
    print(d.__iter__().__next__())
    print(d.__iter__().__next__())
    #输出结果>>>1
    #1
    #1

      当迭代器对象不经过赋值变量就使用next取值时,会发现每次取值都是可迭代对象的第一个值,,因为d.__iter__()时迭代器对象,每一次使用都会对可迭代对象生成新的迭代器对象,所以一直是第一个值。

    d=[1,2,3]
    iter_d = d.__iter__()
    print(iter_d.__next__())
    print(iter_d.__next__())
    print(iter_d.__next__())
    #输出结果>>>1
    #2
    #3

      当其赋值给iter_d时,就可以对其进行迭代了,直到取值完后再取值就会报错,其错误提示是:

    StopIteration

      这是一个异常,可以使用try: except语句对其进行异常处理:

    while True:
        try:
            print(iter_d.__next__())
        except StopIteration:
            # print('输出结束')
            break
    #输出结果>>>1
    #2
    #3
    #输出结束

      当语句成立不报错时,执行语句,当语句报错时,处理异常执行except后的语句。except后是需要处理的异常。

      迭代器取值的特点:只能往后依次取值,不能倒退取值。

      4.for循环内部的本质

      1.将in后面的对象调用__iter__转化为迭代器对象。

      2.使用___next__方法进行迭代取值。

      3.异常捕获并自动结束循环

      迭代取值的优点:1,不依赖于索引取值。2,内存中永远只占一份空间,不会溢出。  

      迭代取值的缺点:1,不能回去指定值,2,取完值后会报错。

      像内置方法中map,zip等方法,其返回值就是一个迭代器对象,这种方式永远只占一份内存,而python2中的这些方法所输出的就仅仅是可迭代对象,所耗内存比较大。

    5。生成器

      本质:生成器就是一种用户自定义的迭代器。

      再普通函数中,其返回值使用return,而在生成器中,使用新的关键字yield。

      yield的功能是,当函数运行时,其中包函yield,加上()后就不会触发函数体内的代码运行。

    def fun9():
        print('111')
        yield 66
        print('222')
        yield 77
        print('333')
        yield 88
        print('444')
        yield 99
    iter1=fun9()
    fun9()
    iter1.__next__()
    print(iter1,fun9,iter1.__next__())
    #输出结果>>>111
    #222
    #<generator object fun9 at 0x000002285BF96D58> <function fun9 at 0x000002285A351E18> 77

      拥有yield的函数中又以下特性:
      1.当函数名()时不会运行函数

      2.只有将函数名()赋值给一个变量时(初始化)才能取值

      3.运行iter1.__next__(),时会运行到yield前停下,

      4,函数名输出的是函数名,初始化赋值的是(函数名())迭代器对象,next打印的是yield后面的值。

      yield既可以返回一个值,也可以返回多个值,多个值按照元组返回。

      所以,yield满足以下特点:

      1.帮你提供了一种自定义生成器方式

      2.会帮你将函数的运行状态暂停住

      3.可以返回值

      return与yield的相同点:

      都可以返回一个或多个值

      不同点:

      yield可以返回多次值,而return只能返回一次。

      yield还可以接受外部传入的值:

    def fun(name):
        print('%s 输入数据'%name)
        print('666')
        while True:
            sj = yield
            print('%s 已输入 %s'%(name,sj))
    g = fun('lzx')
    g.__next__()  
    g.send('11') 
    g.send('22')
    #输出结果>>>lzx 输入数据
    #666
    #lzx 已输入 11
    #lzx 已输入 22

      当使用next运行生成器时,会运行到yield处,当yield被当成参数时,可以使用。send函数对其进行传参,并运行函数。

    6.range

      range是一个可迭代对象,可以用yield手动实现其功能

    def range1(x,y,s=1):
        while x<y:
            yield x
            x+=s
    for i in range1(1,10,1):
        print(i)
    #输出结果>>>1,2,3,4,5,6,7,8,9

    7.生成器表达式:

      如同以下的生成式就是生成器表达式:

    res=(i for i in range(1,10))
    print(res)
    print(res.__next__())
    #输出结果>>><generator object <genexpr> at 0x000002B087176D58>
    #1

      注意,在生成器中,必须通过next触发代码运行,不会主动执行一行代码。

    ·面试题:

    def add(n,i):
        return n+i
    def test():
        for i in range(4):
            yield i
    g=test()
    
    for n in [1,10]:
        g=(add(n,i) for i in g)
    print(n)
    res=list(g)
    print(res)

      求其值。

      当for循环第一次运行时,n值为1所以g=(add(n,i) for i in test())

      当for循环第二次运行时,n值为10,所以g=(add(n,i) for i in (add(n,i) for i in test()))

      但test为迭代器对象,所以,在编写时不会运行,只有在for循环中 遇到next函数时才会运行,那时候的i值已经时10,所以将10带入,结果为:

    [20, 21, 22, 23]

       题2:

    def multipliers():
        return [lambda  x: i*x for i in range(4)]
    print([m(2) for m in multipliers()])

      其中可以将匿名函数分解成闭包函数:

    def outter():
        list1=[]
        for i in range(4):
            def func(x):
                return x*i
            list1.append(func)
        return list1

      在闭包函数的延迟绑定,当函数调用之前,内函数中的变量都不会绑定,函数运行时才会进行绑定,所以最后的i值为3,答案时【6,6,6,6】

      要使得参数传入,需要在内函数参数中添加i:

    def outter():
        list1=[]
        for i in range(4):
            def func(x,i=i):
                return x*i
            list1.append(func)
        return list1
    
    print([m(2) for m in outter()])

      最后结果为[0,2,4,6]

      题3:

      在文件中使用生成器,计算每行的个数。:

      如果使用list存储每一行的字符个数,就需要在内存中存储大量数据:

    with open('text7.txt',mode='w',encoding='utf-8') as f:
        for i in range(0,1000):
            f.write("www.%s.com"%(i)+"
    ")
    
    def func(file):
        with open(file,mode='r',encoding='utf-8') as f:
            list2=[]
            list2.append(len(f.readline()))
        return list2
    print(func('text7.txt'))

      对内存的损耗非常大,所以可以使用生成器,然后对其迭代取值。

    res2 = (len(line) for line in open('test1.txt', 'r', encoding='utf-8'))
    print(res2)  # <generator object <genexpr> at 0x000002B3748FD0A0>
    print(next(res2))
    print(next(res2))

      题4:

    def demo():
        for i in range(4):
            yield i
    g=demo()
    g1=(i for i in g)
    g2=(i for i in g1)
    print(list(g1))
    print(list(g2))

      当g1开始执行是,for循环将其内部指针一一指向g=demo(),所以,当g2,对g1进行遍历时就是个空列表所以答案是:

    [0, 1, 2, 3]
    []
  • 相关阅读:
    bzoj 1103
    [POI] 大都市meg
    [Luogu] 网络
    [Luogu] 1600
    [Luogu] 树状数组
    [Luogu] 软件包管理器
    [Luogu] 遥远的国度
    [USACO5.5] 矩形周长Picture
    [Luogu] 魔板
    【NOIP2015】斗地主
  • 原文地址:https://www.cnblogs.com/LZXlzmmddtm/p/11192005.html
Copyright © 2011-2022 走看看