Python容器、迭代器、生成器

date
Feb 9, 2021
slug
iterator&generator
status
Published
tags
Python
summary
Python容器、迭代器、生成器
type
Post
生成器是python独有的概念,别的语言里面没有。在理解生成器之前,先理解迭代器相关的概念,包括 容器、迭代器协议、可迭代的、 迭代器

容器

容器是保存元素的数据结构,支持成员资格测试。是存在于内存中的数据结构,通常也在内存中保存它们的所有值。在Python中,一些典型的容器例子是
  • list, deque, …
  • set, frozensets, …
  • dict, defaultdict, OrderedDict, Counter, …
  • tuple, namedtuple, …
  • str
从技术上讲,当可以询问对象是否包含某个元素时,它就是一个容器。可以对列表、集或元组等执行此类成员资格测试:
>>> assert 1 in [1, 2, 3] # lists >>> assert 4 not in [1, 2, 3] >>> assert 1 in {1, 2, 3} # sets >>> assert 4 not in {1, 2, 3} >>> assert 1 in (1, 2, 3) # tuples >>> assert 4 not in (1, 2, 3) # 字典数据需要检查key >>> d = {1: 'foo', 2: 'bar', 3: 'qux'} >>> assert 1 in d >>> assert 4 not in d >>> assert 'foo' not in d # 'foo' is not a _key_ in the dict # 字符串 也可以检查子串是否存在 >>> s = 'foobar' >>> assert 'b' in s >>> assert 'x' not in s >>> assert 'foo' in s # a string "contains" all its substrings
大多数的容器都是可迭代对象,可以使用某种方式访问容器中的每一个元素。但并不是所有容器都是可迭代的,比如 Bloom filter
text = 'bairuichangyangna' # 容器 text_iter = iter(text) # 迭代器 print(next(text_iter))

迭代器

  • 迭代器协议:对象需要提供next方法,要么返回迭代中的下一项,要么引起一个StopIteration异常,以终止迭代
  • 可迭代对象:实现了迭代器协议的对象
    • 💡
      协议是一种约定,可迭代对象实现迭代器协议,Python的内置工具(如for 循环, sun, min,max函数等)均使用迭代器协议访问对象
  • 迭代器实现了__iter__和__next__方法的对象都称为迭代器,迭代器是一个有状态的对象,在调用next() 的时候返回下一个值,如果容器中没有更多元素了,则抛出StopIteration异常。

可迭代对象

容器 与 可迭代对象 区别
  • 容器通常是有限的,但可迭代对象也可以表示无限的数据资源
  • 大多是容器都是可迭代的,但可迭代对象并非都是容器。比如,打开的文件,打开的嵌套字也都是可迭代对象
可迭代对象是可以返回迭代器(目的是返回其所有元素)的任何对象,不一定是数据结构。
可迭代 和 迭代器 的区别
>>> x = [1, 2, 3] >>> y = iter(x) >>> z = iter(x) >>> next(y) 1 >>> next(y) 2 >>> next(z) 1 >>> type(x) <class 'list'> >>> type(y) <class 'list_iterator'>
x 是可迭代对象, y 和 z 均是独立的迭代器实例
💡
通常,出于实用的原因,可迭代类将在同一类中同时实现__iter __()和__next __(),并具有__iter __()返回self,这使该类成为可迭代的并且是自己的迭代器。 不过,最好返回一个与迭代器不同的对象。
💡
可迭代对象支持内置函数iter,通过对可迭代对象调用iter函数,会返回一个迭代器,而“迭代器”支持内置函数next,通过不断对其调用next方法,会依次前进到序列中的下一个元素并将其返回,最后到达一系列结果的末尾时,会引发StopIteration异常。补充说明一点,对迭代器调用iter方法,则会返回迭代器自身。

迭代器

迭代器是一个有状态的辅助对象,当在其上调用next()时,它将产生下一个值。 因此,具有__next __()方法的任何对象都是迭代器。 它如何产生值是无关紧要的。
所以迭代器就是值工厂。每次向它询问“下一个”值时,它都知道如何计算它,因为它持有内部状态。
迭代器的例子不胜枚举。所有itertools函数都返回迭代器。
# 有些产生无穷数列 >>> from itertools import count >>> counter = count(start=13) >>> next(counter) 13 >>> next(counter) 14 # 有些从有限的序列产生无限的序列 >>> from itertools import cycle >>> colors = cycle(['red', 'white', 'blue']) >>> next(colors) 'red' >>> next(colors) 'white' >>> next(colors) 'blue' >>> next(colors) 'red' # 有些从无限的序列产生有限的序列 >>> from itertools import islice >>> colors = cycle(['red', 'white', 'blue']) # infinite >>> limited = islice(colors, 0, 4) # finite >>> for x in limited: # so safe to use for-loop on ... print(x) red white blue red
自定义迭代器 生成 数列
>>> class fib: ... def __init__(self): ... self.prev = 0 ... self.curr = 1 ... ... def __iter__(self): ... return self ... ... def __next__(self): ... value = self.curr ... self.curr += self.prev ... self.prev = value ... return value ... >>> f = fib()
注意,这个类既是一个可迭代对象(因为它有一个__iter__()方法),也是它自己的迭代器(因为它有一个__next__()方法)
该迭代器内部的状态完全保存在prev和curr实例变量中,用于后续对迭代器的调用。每次调用next()都会做两件重要的事情
  1. 为下一次next()调用修改它的状态
  1. 为当前调用生成结果
💡
中心思想:从外部看,迭代器就像一个惰性工厂,在你向它请求值之前它是空闲的,这时它开始发出嗡嗡声并产生单个值,之后它再次进入空闲状态。

生成器

生成器是一种特殊的迭代器,任何生成器均是迭代器(反过来不是),但是生成器不需要像迭代器一样实现__iter____next__方法,只需要使用关键字yield或者生成器表达式就可以。
生成器允许编写类似于上面斐波那契序列迭代器示例的迭代器,但语法简洁,避免编写带有iter()next()方法的类。
Python使用生成器对延迟操作提供了支持,延迟操作是指在需要的时候才产生结果,而不是立即产生结果。
Python有两种生成器提供方法:
生成器函数:常规的函数定义,但是使用yield返回而不是return语句返回结果。yield语句一次性返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
生成器表达式:类似列表推导式,但是,生成器返回按需求产生的一个结果,而不是一次构建一个结果列表

生成器函数:

用生成器函数产生斐波那契数列:
def fib(): prev,curr = 0, 1 while True: yield curr prev, curr = curr, prev + curr a = fib() >>>next(a) 1 >>>next(a) 1 >>>next(a) 2 >>>next(a) 3 >>>next(a) 5
fib 就是一个生成器函数,调用该函数时返回对象就是生成器 ga,这个生成器对象的行为和迭代器是非常相似的,可以用在 for 循环等场景中
>>> fib <function __main__.fib()> >>> a <generator object fib at 0x000002A940A4F190>
💡
注意 yield 对应的值在函数被调用时不会立刻返回,而是调用next方法时(本质上 for 循环也是调用 next 方法)才返回