• python3基础笔记(五)迭代器与生成器


    一、迭代器

    1.迭代的概念

    上一次输出的结果为下一次输入的初始值,重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值 

    注:循环不是迭代

    while True: #只满足重复,因而不是迭代
         print('====>')

    2.可迭代的对象

    内置__iter__方法的,都是可迭代的对象。

    list是可迭代对象,dict是可迭代对象,set也是可迭代对象。

    调用__iter__()方法后会生成一个可迭代对象,再调用可迭代对象的__next__()的方法即可遍历这个可迭代对象。

    >>> [1,2].__iter__()
    <list_iterator object at 0x000000000088BA20>
    >>> 'hello'.__iter__()
    <str_iterator object at 0x000000000088B9B0>
    >>> (1,2).__iter__()
    <tuple_iterator object at 0x000000000088BA20>
    >>>
    >>> {'a':1,'b':2}.__iter__()
    <dict_keyiterator object at 0x000000000074B6D8>
    >>> {1,2,3}.__iter__()
    <set_iterator object at 0x00000000008853A8>
    

     例如:

    x = [1, 2, 3]
    y = x.__iter__()    #与 iter(x) 等价 iter() 方法就是调用了 x 对象内部的__iter__()方法
    z = x.__iter__()    
    print(y.__next__()) #与 next(y) 等价 next() 方法调用了 y 对象内部的__next__()方法
    print(y.__next__())
    print(z.__next__())
    print(type(x))
    print(type(y))
    

      输出:

    1
    2
    1
    <class 'list'>
    <class 'list_iterator'>

    这里x是一个可迭代对象,yz是两个独立的迭代器,迭代器内部持有一个状态,该状态用于记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。

    迭代器有一种具体的迭代器类型,比如list_iteratorset_iterator。可迭代对象实现了__iter__方法,该方法返回一个迭代器对象。

    1.为什么要有迭代器?

        对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式。

    2.迭代器定义:

        迭代器:可迭代对象执行__iter__方法,得到的结果就是迭代器,迭代器对象有__next__方法。它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter__()__next__()方法的对象都是迭代器,__iter__()返回迭代器自身,__next__()返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常

    3.迭代器的实现:

    例:

    i=[1,2,3].__iter__()
    
    print(i)     #迭代器
    
    print(i.__next__())
    print(i.__next__())
    print(i.__next__())
    #print(i.__next__()) #抛出异常:StopIteration
     

    输出

    <list_iterator object at 0x1019c3eb8>
    1
    2
    3

      每次调用__next__()方法的时候做两件事: 

    1. 为下一次调用__next__()方法修改状态
    2. 为当前这次调用生成返回结果

      迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

    4.如何判断迭代器对象和可迭代对象

    from collections import Iterable,Iterator
    'abc'.__iter__()
    ().__iter__()
    [].__iter__()
    {'a':1}.__iter__()
    {1,2}.__iter__()
    
    f=open('a.txt','w')
    f.__iter__()
    
    #判断是否为可迭代对象,以下都是
    print("字符串是否为可迭代对象?", isinstance('abc',Iterable))
    print("列表是否为可迭代对象?", isinstance([],Iterable))
    print("元祖是否为可迭代对象?", isinstance((),Iterable))
    print("字典是否为可迭代对象?", isinstance({'a':1},Iterable))
    print("集合是否为可迭代对象?", isinstance({1,2},Iterable))
    print("文件是否为可迭代对象?", isinstance(f,Iterable))
    print('='*50)
    #判断是否为迭代器,只有文件是
    print("字符串是否为迭代器?", isinstance('abc',Iterator))
    print("列表是否为迭代器?", isinstance([],Iterator))
    print("元祖是否为迭代器?", isinstance((),Iterator))
    print("字典是否为迭代器?", isinstance({'a':1},Iterator))
    print("集合是否为迭代器?",isinstance({1,2},Iterator))
    print("文件是否为迭代器?",isinstance(f,Iterator))
    

      

    输出:

    字符串是否为可迭代对象? True
    列表是否为可迭代对象? True
    元祖是否为可迭代对象? True
    字典是否为可迭代对象? True
    集合是否为可迭代对象? True
    文件是否为可迭代对象? True
    ==================================================
    字符串是否为迭代器? False
    列表是否为迭代器? False
    元祖是否为迭代器? False
    字典是否为迭代器? False
    集合是否为迭代器? False
    文件是否为迭代器? True

    可迭代对象:只有__iter__()方法,执行该方法得到的迭代器对象

    迭代器:有__iter____next__()方法

    注:对于迭代器对象来说,执行__iter__()方法,得到的结果仍然是它本身

    5.迭代器的优点和缺点

    优点:
    1.提供了一种不依赖下标的迭代方式
    2.就跌迭代器本身来说,更节省内存

    缺点:
    1. 无法获取迭代器对象的长度
    2. 不如序列类型取值灵活,是一次性的,只能往后取值,不能往前退

    二、三元表达式:

    三元表达式:

    x = 2
    y = 3
    '''
    if x > y:
        print(x)
    else:
        print(y)
    '''
    # 上式可简写为
    res = 'aaaaa' if x > y else 'bbbbbbb'  
    print(res)
    
    
    def max2(x, y):
        #     if x > y:
        #         return x
        #     else:
        #         return y
        return x if x > y else y  # 函数中的应用
    
    print(max2(2, 3))
    

      列表解析:

    1.将字符串内所有字符大写并存入列表中

    # 普通方式
    s = 'hello'
    l = []
    for i in s:
        res = i.upper()
        l.append(res)
    print(l)
    
    # 列表解析方式
    s = 'hello'
    res = [i.upper() for i in s]
    print(res)
    

      输出:

    ['H', 'E', 'L', 'L', 'O']
    ['H', 'E', 'L', 'L', 'O']

    2.筛选列表中大于50的数字并存入一个新的列表

    # 普通方式
    l = [1, 31, 73, 84, 57, 22]
    l_new = []
    for i in l:
        if i > 50:
            l_new.append(i)
    print(l_new)
    
    # 列表解析方式
    res = [i for i in l if i > 50]
    print(res)
    

      输出:

    [73, 84, 57]
    [73, 84, 57]

    3.生成一个0-99的列表

    # 普通方式
    l = []
    for i in range(100):
        l.append(i)
    print(l)
    
    # 列表解析方式
    res = [i for i in range(100)]
    print(res)
    

      输出:

    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

    4.将列表中所有值平方并存入一个新的列表

    # 普通方式
    l = [1, 2, 3, 4]
    l_new = []
    for i in l:
        res = i ** 2
        l_new.append(res)
    print(l_new)
    
    # 列表解析方式
    res = [i ** 2 for i in l]
    print(res)
    

      输出:

    [1, 4, 9, 16]
    [1, 4, 9, 16]

    6.列表解析简单应用:

    # 应用
    l = [1, 31, 73, 84, 57, 22]
    # 两个判断条件
    print([i for i in l if i > 20 and i < 50])
    

      输出:

    [31, 22]

    二、生成器

    • 生成器只能遍历一次!!!!!!!!!

    1.生成器定义

    生成器(generator)是一个特殊的迭代器,它的实现更简单优雅,yield是生成器实现__next__()方法的关键。它作为生成器执行的暂停恢复点,可以对yield表达式进行赋值,也可以将yield表达式的值返回。

    也就是说,yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。

    yield的功能:
    1.相当于为函数封装好__iter__和__next__
    2.return只能返回一次值,函数就终止了,而yield能返回多次值,每次返回都会将函数暂停,下一次next会从上一次暂停的位置继续执行

    例:

    def counter(n):
        print('start'.center(40, '='))
        i=0
        while i < n:
            yield i         # 当程序走到这里返回一个 i 并暂停这个函数。下一次从这里继续执行。
            i+=1
        print('end'.center(40, '='))    # 这句不会执行。
    
    
    g=counter(5)    # 获得生成器
    print(g)
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    # print(next(g))   #会报错
    

      输出:

    ================start...================
    0
    1
    2
    3
    4

     总结:

      生成器:只有在调用的时候才会生成相应的数据(调用到这个数据的时候才会生成这个数据,没有调用到时就没有这个数据)
      只记录数据的当前位置

      生成器不能像普通的列表一样,通过下标或者切片的方式去取
      生成器只能通过 循环 或者__next__()方法去取。

    2.生成器函数

    • 生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
    • 含有yield的函数叫做生成器函数。  在调用生成器函数时只会返回一个生成器的对象,不会运行生成器函数内的代码。需要使用send()函数或者__next__()函数才会执行生成器内部的函数。

    普通函数return返回

    def lay_eggs(num):
        egg_list=[]
        for egg in range(num):
            egg_list.append('鸡蛋%s' %egg)
        return egg_list
    
    yikuangdan=lay_eggs(10) #我们拿到的是蛋
    print(yikuangdan)
    

      输出:

    ['鸡蛋0', '鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']

    迭代器函数:

    def lay_eggs(num):
        for egg in range(num):
            res='鸡蛋%s' %egg
            yield res        # 生成器关键语法
            print('下完一个蛋')
    
    laomuji=lay_eggs(10) #我们拿到的是一只母鸡
    print(laomuji)
    print(laomuji.__next__())       # 迭代  鸡蛋0
    print(laomuji.__next__())       # 鸡蛋1
    print(laomuji.__next__())       # 鸡蛋2
    egg_l=list(laomuji)             # 将剩余的转换成一个列表
    print(egg_l)
    

      输出:

    鸡蛋0
    下完一个蛋
    鸡蛋1
    下完一个蛋
    鸡蛋2
    下完一个蛋
    下完一个蛋
    下完一个蛋
    下完一个蛋
    下完一个蛋
    下完一个蛋
    下完一个蛋
    下完一个蛋
    ['鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9']

     

    生成器函数:

    • food=yield food_list

      #g.send('food1'),先把food1传给yield,由yield赋值给food,然后返回给food_list,然后再往下执行,直到再次碰到yield,然后把yield后的返回值返回给food_list

    注意:开始生成器不能send非空值

    def eater(name):        #协程函数
        print('%s ready to eat' %name)
        food_list=[]
        while True:
            food=yield food_list           #装饰器表达式
            food_list.append(food)
            print('%s start to eat %s' %(name,food))
    
    
    g=eater('zjs')
    print(g)        #生成器
    
    print(g.send('food1'))  #传值
    

      输出:

    Traceback (most recent call last):
    <generator object eater at 0x00000000006FE4C0>
      File "E:/zjs/Python/pythonTest/day01/day01/filetest.py", line 292, in <module>
        print(g.send('food1'))  #传值
    TypeError: can't send non-None value to a just-started generator

    初始化后:

    def eater(name):        #协程函数
        print('%s ready to eat' %name)
        food_list=[]
        while True:
            food=yield food_list           #装饰器表达式
            food_list.append(food)
            print('%s start to eat %s' %(name,food))
    
    
    g=eater('zjs')
    print(g)        #生成器
    next(g) #等同于 g.send(None),初始化
    
    print(g.send('food1'))
    

      输出:

    <generator object eater at 0x00000000010EE4C0>
    zjs ready to eat
    zjs start to eat food1
    ['food1']
    • 为了防止忘记初始化,可利用装饰器进行初始化,如下
    复制代码
    def deco(func):     #初始化函数
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            next(res)          #等同于 g.send(None),初始化
            return res
        return wrapper
    
    @deco       #用初始化函数装饰器,调用初始化函数
    def eater(name):        #协程函数
        print('%s ready to eat' %name)
        food_list=[]
        while True:
            food=yield food_list           #装饰器表达式
            food_list.append(food)
            print('%s start to eat %s' %(name,food))
    
    
    g=eater('hexin')
    # print(g)        #生成器
    # next(g) #等同于 g.send(None),初始化
    
    print(g.send('food1'))
    print(g.send('food2'))
    print(g.send('food3'))
    复制代码

    输出

    复制代码
    hexin ready to eat
    hexin start to eat food1
    ['food1']
    hexin start to eat food2
    ['food1', 'food2']
    hexin start to eat food3
    ['food1', 'food2', 'food3']
    复制代码
     
     
     

    3.生成器表达式

    • 生成器表达式:类似于列表解析,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表;

      

    l = [i for i in range(3)]  # 生成一个列表
    print(l)
    
    g = (i for i in range(3))  # 生成器表达式 产生一个生成器。使用这个对象会带有__next__()方法。
    print(g)
    print(g.__next__())
    print(g.__next__())
    print(g.__next__())
    #print(g.__next__())    # 如果容器中没有更多元素了,抛出StopIteration异常
    

      输出:

    [0, 1, 2]
    <generator object <genexpr> at 0x000000000112E4C0>
    0
    1
    2

      使用生成器表达式产生的生成器会自带__next__()方法不必在将次对象__iter__() 使用__iter__()方法返回的仍然是自己。

    
    
    
    
  • 相关阅读:
    Android自定义drawable(Shape)详解
    如何设置对话框的宽度和高度
    Android资料之-EditText中的inputType
    android4.0 禁止横竖屏切换使用 android:configChanges="orientation|keyboardHidden"无效的解决方法
    android ScrollView 充满屏幕
    治疗神经衰弱最有效的方法和药物是什么
    交换机和路由器的区别
    小众编程语言同样值得你关注
    RotateAnimation详解
    你可能没听过的 Java 8 中的 10 个特性
  • 原文地址:https://www.cnblogs.com/zbuter/p/8724226.html
Copyright © 2020-2023  润新知