可迭代对象、迭代器、生成器傻傻分不清楚
本文作者:陈志玲
文字编辑:钱梦璇
导读
在Python编程中,我们总是可以见到iterable、iterator和generator,但是却不是很能理解。为了更好地理解后面介绍的内置函数,我们今天先来大概的了解一下什么是可迭代对象、生成器以及迭代器。其实三者在程序算法中的使用非常普遍,但是使用者可能并不知道自己使用的就是这些,那么接下来,我们就一起来一探究竟。
1. 可迭代对象(Iterable)
所谓迭代,就是访问数据元素的一种方式,是当我们需要依次拿到数据进行使用时,对数据进行遍历的过程。而可迭代对象并不是指某种可以进行迭代的具体数据类型,它是一种容器对象。
判别一个对象是否为可迭代对象,最简单的方式就是可以通过for…in…语句进行遍历,如果它可以通过for循环来遍历并提供数据来使用就是可迭代对象。而我们常见的可迭代对象包括:list、tuple、dict、set、str等,以及下面介绍的生成器也是可迭代对象。
(1)例如list
In [1]: a=[1,2,3] #a是一个列表
In [2]: for i in a:
...: print(i)
...:
1
2
3
In [3]: b={"1":"a","2":"c"}
In [4]: for key in b:
...: print(key)
...:
1
2
In [5]: c=int(20.2)
In [6]: for i in c:
...: print(i)
...:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-491040f6185b> in <module>
----> 1 for i in c:
2 print(i)
3
TypeError: 'int' object is not iterable
当然我们也可以通过isinstance()来判断一个对象是否是Iterable对象。具体用法如下:
In [11]: from collections import Iterable #导入collections模块中Iterable
In [12]: isinstance(a,Iterable)
Out[12]: True
In [13]: isinstance(b,Iterable)
Out[13]: True
In [14]: isinstance(c,Iterable)
Out[14]: False
2. 生成器(Generators)
了解生成器之前,先大概了解一下列表生成式。列表生成式(List Comprehensions),是Python中内置的非常简单却可以创建list的生成式。例如:
In [16]: [x*x for x in range(1,4)]
Out[16]: [1, 4, 9]
而当我们需要创建一个非常大的容量的列表时,生成器就应运而生。为了节省内存空间,生成器可以一边循环一边计算,即在循环过程中不断推算出后续的元素(用到再计算),不需要创建一个完整的列表存储所有计算好的结果,从而节省空间。创建一个生成器也很简单,只需将列表生成式的方括号换成圆括号,例如:
In [17]: g=(x*x for x in range(1,4))
In [18]: g
Out[18]: <generator object <genexpr> at 0x0000024695A2EED0>
既然是用到才计算,生成器的元素就需要通过next()函数来获得,即:
In [20]: next(g)
Out[20]: 1
In [21]: next(g)
Out[21]: 4
因为genertor保存的是算法,每次都需要next(g)来调用,计算出g的下一个元素值,但是当我们调用完所有的元素时,它并不会停止,而是抛出一个错误:
In [22]: next(g)
Out[22]: 9
In [29]: next(g)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-29-e734f8aca5ac> in <module>
----> 1 next(g)
StopIteration:
这样调用确实很麻烦,但是我们可以用for循环,因为生成器也是可迭代对象。
In [31]: for i in (x*x for x in range(1,4)):
...: print(i)
...:
1
4
9
生成器还有另一个生成方法,即如果一个函数定义中包含有yield关键字,则这个函数就是一个生成器。
In [32]: def f():
...: yield 1
...: yield 2
...: yield "Error"
In [33]: f()
Out[33]: <generator object f at 0x00000246959A6DE0>
同样函数f()的调用也是通过next():
In [34]: next(f())
Out[34]: 1
我们已经知道了什么可迭代对象和生成器,那什么是迭代器呢?能够被next方法调用并返回下一个值的就是迭代器。那么可想而知,生成器就是一种迭代器。迭代器中存放着访问当前对象数据的方法,每次调用可以访问一个数据。这里也可以使用isinstance()来判断一个对象是否是Iterator对象,例如判断上面的g生成器:
In [36]: from collections import Iterator
In [37]: isinstance(g,Iterator)
Out[37]: True
但是可迭代对象并不一定是迭代器,但我们可以通过iter()函数将其变成迭代器。例如列表a:
In [38]: isinstance(a,Iterator)
Out[38]: False
In [40]: isinstance(iter(a),Iterator)
Out[40]: True
4. 总结
Ø 凡是可作用于for循环的对象都是可迭代对象;
Ø 凡是可作用于next()函数的对象都是迭代器类型,它们表示一个惰性计算的序列;
Ø 集合数据类型如list、tuple、set、dict、str等是可迭代对象但不是迭代器;
Ø 可以通过iter()函数将一个可迭代对象转换成迭代器对象,目的是减少占用内存;
Ø 迭代器对象一定是可迭代对象,但可迭代对象不一定是迭代器对象;
Ø 生成器是高级的迭代器,也是可迭代对象。
以上概念你都清楚了吗?
关于我们
微信公众号“Stata and Python数据分析”分享实用的stata、python等软件的数据处理知识,欢迎转载、打赏。我们是由李春涛教授领导下的研究生及本科生组成的大数据处理和分析团队。
1)必须原创,禁止抄袭;
2)必须准确,详细,有例子,有截图;
注意事项:
1)所有投稿都会经过本公众号运营团队成员的审核,审核通过才可录用,一经录用,会在该推文里为作者署名,并有赏金分成。
2)邮件请注明投稿,邮件名称为“投稿+推文名称”。
3)应广大读者要求,现开通有偿问答服务,如果大家遇到有关数据处理、分析等问题,可以在公众号中提出,只需支付少量赏金,我们会在后期的推文里给予解答。