for循环的底层原理;迭代器(Iterator),可迭代对象(Iterable),生成器( generator)

1. 迭代(Iteration)与可迭代对象

如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

2. 一种节省内存的可迭代对象generator->包括生成器带yield的generator function

2.1 生成器

受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器

generator保存的是算法,生成器不能直接打印,需要next()函数获得下一个元素,直到抛出StopIteration异常,或者使用for循环进行遍历,因为generator也是可迭代的(Iterable)

因为不断调用next()太麻烦了,所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误

2.2 带yield的genator function

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator函数,调用一个generator函数将返回一个generator

这里,最难理解的就是generator函数和普通函数的执行流程不一样。普通函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)


"""
>>> g = odd()
>>> next(g)
step 1
1
>>> next(g)
step 2
3
>>> next(g)
step 3
5
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
"""

odd不是普通函数,而是generator函数,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(g)就报错

3. 迭代器(Iterator)

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

生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

迭代器和可迭代对象的关系

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象

image

为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

两者可以通过isinstance()函数判断

>>> from collections.abc import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False

>>> from collections.abc import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

for循环本质

Python的for循环本质上就是通过不断调用next()函数实现的,例如:

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:
        # 遇到StopIteration就退出循环
        break

Reference

  1. python doc -> Built-in Functions -> next()
  2. 廖雪峰-生成器
posted @ 2024-03-09 09:52  光辉233  阅读(308)  评论(0)    收藏  举报