Iterables,Iterators和Generators
文章目录
最近一段时间都在研究python里边的一些概念:iterator,iterable和generator.看到这篇文章写的十分不错,基本上讲清楚了这三个东西都是什么以及他们之间的关系.这当然值得记录一下😊.
所谓一图胜千言,这篇文章中的这个关系图让人一目了然.
纠结所在
如果对iterator,iterable和generator的概念比较纠结,一般主要是没有区分清楚:
- container
- iterable
- iterator
- generator
- generator expression
- [list,set,dict] comprehension
下面按照原文中讲解的那样,对上边几个概念逐一解释:
container
在python中container是一类用于存放各种元素(item)的容器,并且它能够判断一个item是否在container中.一般来说它会将它内部的元素都存放在内存中,并且能够逐一返回它内部的每个元素.一些比较典型的类型都是container:
- list,deque…
- set,fronzensets…
- dict,defaultdict,OrderedDict,Counter…
- tuple,namedtuple…
- str
container相对来说比较容易理解,因为它就像是现实生活中的一些真实容器,用来存放各种东西.并且container可以很容易的判断一个item是否是它的一个元素,几个🌰:
- 对于list、set、tuple
|
|
- 对于dict,可以直接判断一个元素是否是它的key
|
|
- 对于string,可以判断一个字符或者子串是否是这个string的组成部分
|
|
上边一下例子说明对于container来说,判断一个item是否是它其中的一个元素十分直接、方便.尤其对于string来说,虽然它并没有将它的所有子串都放在内存中,但也是可以通过in
来进行子串检测.
但是需要注意的是:上边我们关注的主要是container的成员检测
,并且一般来说,container也都可以逐一返回它内部的每个元素,比如:
|
|
但能逐一返回container中每个元素并不是因为它是一个container(有点绕😄),这是因为它是可迭代的(iterable).所有的container都是iterable的.正如上边图中所示的那样a container typically is an iterable
.
iterable
如上所述,所有的container都是iterable的,但是除了各种类型的container以外,像file或者socket这样类型的对象也都是iterable.与container之间不同的是,container大都是包含着有限个元素的,或者说他们有着明确的长度,但是像file或者socket这样的对象他们内部可能会有不那么确定数量的数据.
所以,一个iterable的对象必须是一个能够逐一返回自身包含元素的对象,而这个对象被称为iterator.
iterable和iterator之间的区别可以通过下边的🌰看出:
|
|
上边的例子中,x是一个iterable(可迭代对象),y和z是两个不同的iterator(迭代器).y和z能够分别逐一返回x中的每个元素,并且这两个iterator之间不会互相影响自己的状态.
通常情况下,一个iterable class会在内部实现
__iter__()
和__next()__
方法,这个过程也可以成为实现了迭代器协议,其中__iter__()
方法返回迭代器对象iterator,__next()__
实现逐一返回元素的过程.
所以,在使用for item in iterator
这样的语句进行遍历的时候,其实执行过程类似于下图:
iterator
所以说,到底iterator是个什么东西?🐍
一个iterator对象可以当作是一个工厂,每次使用next()方法,都会产生一个来自iterator内部的值,并且它能够维护于此相关的内部状态.
iterator内部的元素有时候是有限的,但有时候也可能包含无限个数的元素:
|
|
利用itertools中的一些方法,可以利用有限元素的iterator变为无限元素的iterator:
|
|
有的时候,可以在无限元素的iterator中分出一个有限元素的iterator:
|
|
通过构造一个斐波那契数列来看iterator对内部状态是如何控制的:
|
|
可以看到class fib 实现了迭代器协议,所以f是一个iterator,并且是iterable的.通过两个内部变量prev
和curr
,fib能够在每次相应next()
调用的时候返回对应的值.每次对next()
的响应,对于iterator来说主要有两件事情要做:
- 为下一次
next()
调用改变内部状态 - 生成当前
next()
调用要返回的值
由此可以看出,iterator可以当作是一个惰性求值的工厂,每次
next()
调用会让iterator生成所需要的值,然后在下一次next()
调用前都保持空闲状态.🕶️
generator
generator(生成器),可以当作一种特殊的iterator(更为优雅的一种).
它能够用更为简洁的代码来实现上边斐波那契数列,并且避免手动实现__iter()__
和__next()__
.
举个🍐:
|
|
通过yield
来构造的generator,是不是更加优雅.💍
通常有两种方法能够构造一个generator:
- generator function
- generator comprehension
generator function
很好理解,表面上和普通的函数没什么区别,只是这个函数通过关键字yield
来返回值.
类似于列表推导式或者set、dict,通过(x for x in rang(10))
这样的形式返回的就是一个generator,而通过圆括号()
进行的推导式就是generator comprehension
.
总的来说,generator能够非常有效的组织代码,用更少的变量和流程控制,并且使用generator会更好地减少内存或者cpu的负载.所以像我这样的代码里边遍布了各种for循环:
|
|
还是抓紧时间好好改一改吧:
|
|
文章作者 rgozi
上次更新 2017-08-10