Python基础——迭代器和生成器

生成器(generator)的几种创建方法:

1. 把列表生成式中的'[]'改成'()'

>>> g = ((x ** 2 + y ** 2) for x in range(3) for y in range(3))
>>> g
 at 0x0000023EA02EC2B0>

2. 使用yield的常规函数

from collections import Generator, Iterable
from types import GeneratorType
from inspect import isgeneratorfunction


def fib(n):
    m, a, b = 0, 0, 1
    while m < n:
        yield b
        a, b = b, a + b    # 相当于:t = (b, a + b)    a = t[0]    b = t[1]
        m += 1


# 查看生成器的地址
print('fib(17)的地址', fib(17), sep=' --- ')
# 判断fib(17)、fib是不是生成器的两种方法
print('fib(17)是生成器吗?', isinstance(fib(17), Generator), sep=' --- ')
print('fib(17)是生成器吗?', isinstance(fib(17), GeneratorType), sep=' --- ')
print('fib是生成器吗?', isinstance(fib, Generator), sep=' --- ')
# 判断fib是不是生成器函数
print('fib是生成器函数吗?', isgeneratorfunction(fib), sep=' --- ')
# 判断fib是不是可迭代的
print('fib是可迭代的吗?', isinstance(fib, Iterable), sep=' --- ')
# 判断fib(17)是不是可迭代的
print('fib(17)是可迭代的吗?', isinstance(fib(17), Iterable), sep=' --- ')
# 注意:生成器和生成器函数之间的关系就好比类与类的实例之间的关系

print('生成斐波那契数列的前17个数:')
print('    ', end='')
for x in fib(17):
    print(x, end='—>')
print('...')

生成器是一个返回迭代器的函数,只能用于迭代操作。一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。

3. 生成器的练习题
练习:把杨辉三角的每一行看作一个list,写一个generator,不断输出下一行的list

def triangles(rows):
    # 生成一个初始矩阵,rows行,1列的矩阵 --- [[1], [1], [1], [1], ...]
    tri = [[x] for x in (1,) * rows]
    # 对矩阵的每一行进行扩展,第一行一列,第二行两列,第三行三列...
    for row in range(rows):
        # 第一行只有一个元素
        if row == 0:
            pass
        # 第二行两个元素,第一个和最后一个都是1
        elif row == 1:
            tri[row].append(1)
        else:
            # 第三行开始第i个元素等于上一行第i-1个元素和第i个元素之和
            for col in range(1, row):
                tri[row].append(tri[row-1][col-1]+tri[row-1][col])
            # 每行的最后一个元素都是1
            tri[row].append(1)
        # 迭代时返回以行为单位的列表
        yield tri[row]


results = []
# 对triangles(10)进行迭代,没返回一个列表就存储在results中
for t in triangles(10):
    print(t)
    results.append(t)
# 对生成器triangles(10)进行测试
if results == [
    [1],
    [1, 1],
    [1, 2, 1],
    [1, 3, 3, 1],
    [1, 4, 6, 4, 1],
    [1, 5, 10, 10, 5, 1],
    [1, 6, 15, 20, 15, 6, 1],
    [1, 7, 21, 35, 35, 21, 7, 1],
    [1, 8, 28, 56, 70, 56, 28, 8, 1],
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
    print('测试通过!')
else:
    print('测试失败!')

4. 迭代器

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

· 迭代是访问集合元素的一种方式
· 迭代器是一个可以记住遍历的位置对象
· 迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问完结束。迭代器只能往前不会后退
· 迭代器有两个基本的方法:iter()和next()
· 字符串、列表或元组对象都可以用于创建迭代器
· 字典创建迭代器时迭代的是字典的键(key)

可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。(可迭代的判断在第2个内容中)

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator,因为:Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

把list、dict、str等Iterable变成Iterator可以使用iter()函数:

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
for x in [1, 2, 3, 4, 5]:
    pass
# 完全等价于
# 获取Iterator对象
it = iter([1, 2, 3, 4, 5])
while True:
    try:
        # 获取下一个值
        x = next(it)
    except StopIteration:
        break

你可能感兴趣的:(Python基础——迭代器和生成器)