8、迭代器与生成器
1、什么是迭代
使用for循环遍历取值的过程叫做迭代
- 迭代列表
list1 = [11, 22, 33, 44]
for i in list1:
    print(i)
# 11
# 22
# 33
# 44
- 迭代元组
tuple1 = (11, 22, 33, 44)
for i in tuple1:
    print(i)
# 11
# 22
# 33
# 44
- 迭代字符串
str1 = "abcde"
for i in str1:
    print(i)
# a
# b
# c
# d
# e
- 迭代字典
dic1 = {'name': 'zht', 'age': 25, 'school': 'zhbit'}
- -默认迭代的是 键
for i in dic1:
    print(i)
# name
# age
# school
- 
- 迭代字典的值
 
for i in dic1.values():
    print(i)
# zht
# 25
# zhbit
- 
- 迭代字典的键和值
 
for i1, i2 in dic1.items():
    print(i1, i2)
# name zht
# age 25
# school zhbit
- 迭代集合
s = {'apple', 'banana', 'pear', 'orange'}
for i in s:
    print(i)
# apple
# pear
# orange
# banana
2、可迭代对象(Iterable)
可以使用for循环遍历取值的对象(迭代是过程,可迭代对象是对象),就是可迭代对象。如上面的例子,列表、元组、字典、集合、字符串都是可迭代对象
可以使用 isinstance() 函数判断该对象是否是可迭代对象
In:     from collections import Iterable # 引入模块
In:     isinstance([1,2,3,4],Iterable) # 判断列表是否是可迭代对象                 
Out:    True   
In:     isinstance((1,2,3,4),Iterable) # 判断元组是否是可迭代对象
Out:    True
In:     isinstance("abcdef",Iterable) # 判断字符串是否是可迭代对象
Out:    True
In:     isinstance({'name':'zht','age':25,'school':'zhbit'},Iterable)  # 判断字典是否是可迭代对象
Out:    True
In:     isinstance(100,Iterable)  # 判断数字(Numbers)是否是可迭代对象
Out:    False
3、推导式
list(range(1, 5))  # 注意range(1, 5)是左闭右开,1 2 3 4
# [1, 2, 3, 4]
[x*x for x in range(1, 5)]
# [1, 4, 9, 16]
[x for x in range(1, 10) if x % 2 == 0]
# [2, 4, 6, 8]
# 过滤掉长度小于等于3的字符串列表,并将剩下的转换成大写字母
names = ['Bob', 'Tom', 'alice', 'Jerry', 'Wendy', 'Smith']
[name.upper() for name in names if len(name) > 3]
# ['ALICE', 'JERRY', 'WENDY', 'SMITH']
# 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
[(x, y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 != 0]
# [(0, 1), (0, 3), (0, 5), (2, 1), (2, 3), (2, 5), (4, 1), (4, 3), (4, 5)]
4、生成器
通过列表生成式,我们可以直接创建一个列表。但是假如我们有如下推导式,创建100万个元素的列表。这个list就占用很大内存。如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
[x*x for x in range(1, 1000000)]
所以,如果列表元素可以按照某种算法推算出来(推导式或函数),那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。
在Python中,这种一边循环一边计算的机制,称为生成器(
generator)
4.1、创建生成器
- 方法1:即把列表生成式的[ ]改成( )——生成器表达式
推导式:
l=[x for x in range(1, 10) if x % 2 == 0]
l
# [2, 4, 6, 8]
print(type(l))
# <class 'list'>
# 通过for循环打印每个元素
for i in l:
    print(i)
# 2
# 4
# 6
# 8
生成器:把列表推导式的[ ] 改成( )
g=(x for x in range(1, 10) if x % 2 == 0)
g  # 如下结果,generator object,即生成器对象
# <generator object <genexpr> at 0x000001E0798AB040>
print(type(g)) # 打印g的类型
# <class 'generator'>
生成器保存的是算法,可以通过next()函数获得生成器的下一个元素的值。每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
next(g)
# 2
next(g)
# 4
next(g)
# 6
next(g)
# 8
next(g)
# StopIteration: 
我们创建了一个生成器后,基本上永远不会用next()调取,而是通过for循环来迭代它,并且不需要关心StopIteration的错误。既然生成器可以被for循环迭代,那么生成器也是可迭代对象。
g1 = (x for x in range(1, 10) if x % 2 == 0) # 注意range(1, 10)是左闭右开,1 2 3 4 5 6 7 8 9
for i in g1:
    print(i)
# 2
# 4
# 6
# 8
- 方法2:函数加 yield()关键字——生成器函数
比如我们有一个f1(max)函数,输入最大值,然后从0开始打印至最大值。如下:
def f1(max):
    i=0
    while i <=max:
        print(i)
        i+=1
    return None
f1(10)
# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10
那如何将f1(max)函数修改成生成器呢?只需要把 print(i) 改为 yield(i) 就可以了
- 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个生成器。
- 区分普通函数和生成器函数,普通函数调用直接返回结果,生成器函数的“调用”实际返回一个生成器对象。
def f1(max):
    i=0
    while i <=max:
        yield(i)  # yield关键字
        i+=1
    return None
f1(10)
# <generator object f1 at 0x000001E078DE24A0>
for i in f1(10):   # 通过for循环调出
    print(i)
# 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# 10
我们在定义一个与f1一样的生成器f2,并用next函数调用,如下:
def f2(max):
    i=0
    while i <=max:
        yield(i)
        i+=1
    return None
g = f2(10)
next(g)
# 0
next(g)
# 1
next(g)
# 2
next(g)
# 3
4.2、生成器的执行流程
- 函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
- 变成生成器的函数,每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
In:     def gen():
            print("步骤1")
            yield(1)
            print("步骤2")
            yield(3)
            print("步骤3")
            yield(5)
        
        g=gen()
        print(type(g))
Out:    <class 'generator'>
In:     next(g)
Out:    步骤1
        1
In:     next(g)
Out:    步骤2
        3
In:     next(g)
Out:    步骤3
        5
In:     next(g)
Out:    ---------------------------------------------------------------------------
        StopIteration                             Traceback (most recent call last)
        <ipython-input-24-e734f8aca5ac> in <module>
        ----> 1 next(g)
        
        StopIteration: 
5、迭代器
- 我们现在已经知道,List、Tuple、Dict、Set、Str等数据类型、以及生成器都可以用for进行迭代,故他们都是可迭代对象。
- 生成器不但可以被for进行迭代,还可以被next()函数进行调用,直到抛出StopIteration异常
- 可以被next()函数调用并不断返回下一个值的对象称为迭代器
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,迭代器只能往前不会后退
- 迭代器有两个基本的方法:iter()和next()
为什么
List、Tuple、Dict、Set、Str等数据类型是可迭代对象,但不是迭代器呢?
- 这是因为迭代器对象是一个数据流,里面只保存算法
- 可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过
next()函数实现按需计算下一个数据,所以迭代器的计算是惰性的,只有在需要返回下一个数据时它才会计算- 迭代器的甚至可以表示一个无限大的数据流,例如全体自然数。而使用
list等数据类型是永远不可能存储全体自然数的
- 可以利用iter()函数将List、Tuple等数据类型转化为迭代器
In:     # 字符串创建迭代器
        str1="abcdefgh"
        iter1=iter(str1)
        print(type(iter1))
Out:    <class 'str_iterator'>  
In:     # 列表创建迭代器
        list1=[1,2,3,4,5]
        iter2=iter(list1)
        print(type(iter2))
Out:    <class 'list_iterator'>
In:     # 元组创建迭代器
        tup1=(1,2,3,4,5)
        iter3=iter(tup1)
        print(type(iter3))
Out:    <class 'tuple_iterator'>
6、迭代器和生成器的区别
共同点
- 生成器与迭代器,都可以被next()调取,故生成器也是一种迭代器。
不同点
- 语法上:
 生成器是通过函数的形式中调用yield或()的形式创建的(生成器函数与生成器表达式);而迭代器可以通过iter()内置函数创建。
- 用法上:
 生成器在调用next()函数或for循环中,所有过程被执行,且返回值;迭代器在调用next()函数或for循环中,所有值被返回,没有其他过程或动作。

 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号