Python 的生成器与迭代器
0

1. 生成器generator

概念
通过列表推导式可以直接创建一个列表,但是受到内存限制,列表容量肯定是有限的,而且一个超大的列表不仅占用很大内存空间,当我们只需要访问该列表的前几个元素时,后边元素所占用的空间就都浪费掉了。
生成器可以解决以上问题,在循环访问生成器的过程中,每次生成器只计算出并提供循环需要的下一个元素,直到循环终止或生成器没有下一个元素了(此时生成器抛出 StopIteration 异常)。
创建一个生成器并访问
只要把一个列表推导式的 [] 改成 () ,就创建了一个生成器;

l = [x * x for x in range(3)]
print(l)
g = (x * x for x in range(3))
print(g)

输出结果为:

[0, 1, 4]
<generator object at 0x00000200D5006A20>

可以通过next()函数获取生成器的下一个元素,当没有下一个元素时,得到 一个StopIteration异常;

print(next(g))
print(next(g))
print(next(g))
print(next(g))

输出结果为:

0
1
4
Traceback (most recent call last):
File "C:/Users/17966/PycharmProjects/test/test.py", line 9, in
print(next(g))
StopIteration

但在实际应用中,不会通过next()函数获取生成器的元素,而是通过for循环获取,for循环的内部机制执行了next()函数的调用及StopIteration异常的处理;

for i in g:
  print(i)

输出结果为:

0
1
4

用函数实现生成器
如果一个函数了定义中包含了yield关键字,它就不再是一个普通的函数,而是一个生成器;

# 一个无限长的斐波那契数列的生成器
def fib():
  a = 0
  b = 1
  while True:
    yield b
    a, b = b, a + b
####
print(fib())
# 获取100以内的斐波那契数列
for i in fib():
  if i > 100: break
  print(i)

输出结果为:

<generator object fib at 0x000001E5D5B26A20>
1
1
2
3
5
8
13
21
34
55
89

# 一个可指定长度的的斐波那契数列的生成器
def fib(max):
    a = 0
    b = 1
    while b < max:
        yield b
        a, b = b, a + b
####
f = fib(50)
print(f)
# 获取50以内的斐波那契数列
for i in f:
    print(i)

输出结果为:

<generator object fib at 0x00000225771C6A20>
1
1
2
3
5
8
13
21
34

在使用函数实现的生成器的过程中,每获取一次下个元素,生成器函数就执行到yield语句并中断,同时返回yield提供的值作为元素值;当再次获取下个元素时,中断恢复继续往下执行,直到再次执行到yield语句;如此往复,直到循环终止不在需要下一个元素;或生成器没有了下一个元素返回StopIteration异常,即函数运行结束不再有yield语句,这种情况下,函数的返回值可以从异常对象实例的value属性中获取。

2. 迭代器Iterator

可以直接作用于for循环的对象统称为可迭代对象Iterable
不但可以作用于for循环,还可以被next()函数不断调用获取下一个元素的对象称为迭代器Iterator
生成器都是迭代器;

from collections import Iterable, Iterator
print(isinstance([1, 2], Iterable))
print(isinstance((1, 2), Iterable))
print(isinstance({'a':1, 'b':2}, Iterable))
print(isinstance('abc', Iterable))
print(isinstance(100, Iterable))
print(isinstance((x*x for x in range(5)), Iterable))
print('//////////////////////')
print(isinstance([1, 2], Iterator))
print(isinstance((1, 2), Iterator))
print(isinstance({'a':1, 'b':2}, Iterator))
print(isinstance('abc', Iterator))
print(isinstance(100, Iterator))
print(isinstance((x*x for x in range(5)), Iterator))
print('//////////////////////')
print(isinstance(iter([1, 2]), Iterator))
print(isinstance(iter((1, 2)), Iterator))

输出结果为:

True
True
True
True
False
True
//////////////////////
False
False
False
False
False
True
//////////////////////
True
True

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

讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!