关于python的可迭代对象、迭代器和生成器

容器

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in,not in 关键词判断元素是否包含在容器中。简单地理解,你可以把容器当成一个房子,我们可以放很多东西进去。

python中的很多数据结构都是容器。比如,字符串(str)、列表(list)、字典(dict)和元组(tuple),还有特殊的一种数据结构,集合(set)等。下图可以把这几个数据类型进行简单的分类。

在这里插入图片描述
前面说到,我们可以用in或者not in来判断一个元素是否在容器中,其实这种能力不是容器本身具有的,而是而是可迭代对象赋予了容器这种能力。但并不是所有容器都是可迭代的,比如Bloom filter,虽然Bloom filter可以用来检测某个元素是否包含在容器中,但是并不能从容器中获取其中的每一个值,因为Bloom filter压根就没把元素存储在容器中,而是通过一个散列函数映射成一个值保存在数组中。

可迭代对象

我们在学python中,经常用到for in循环来获取里面每个元素。比如:

In [1]: results = [1, 2, 3, 4]

In [2]: for result in results:
  ...:     print(result)
  ...:
1
2
3
4

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

如何判断一个对象是否可迭代呢,即是这个对象是否是可迭代对象。这里可以通过collections模块中的Iterable类型判断:

In [3]: from collections import Iterable

In [4]: isinstance([1,2,3,4],Iterable)
Out[4]: True

In [5]: isinstance('abc',Iterable)
Out[5]: True

In [6]: isinstance({'a':1, 'b':2},Iterable)
Out[6]: True

In [7]: isinstance(1234,Iterable)
Out[7]: False

注:列表生成式,使用频率也是非常之高。

列表生成式式Python内置的非常简单却强大的可以用来创建list的生成式。运用列表生成式,可以写出非常简洁的代码。

In [8]: list(x * x for x in range(1, 11))
Out[8]: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

In [9]: [x * x for x in range(1, 11)]
Out[9]: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

In [10]: [x * x for x in range(1, 11) if x % 2 == 0]
Out[10]: [4, 16, 36, 64, 100]

迭代器

能够被next()函数调用,并且返回下一个值的对象称为迭代器:Iterator。
它是一个带状态的对象,它能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter__和__next__()方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常,至于它们到底是如何实现的这并不重要。

所以,迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值得时候给你返回。

判断一个对象是都是迭代器,可以通过collections模块的Iterator类型判断。

>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([123], Iterator)
False
>>> isinstance({'a':1, 'b':2}, Iterator)
False
>>> isinstance('abc', Iterator)
False

迭代器可以通过next()函数或者__next__()方法获取下一个值。

生成器

当我们调用一个普通的python函数时(其实不光是python函数,绝大部分语言的函数都是如此),一般都是从函数的第一行开始执行,直到遇到return语句或者异常或者函数的最后一行。这样,函数就将控制权交还于调用者,函数中的所有工具以及局部变量等数据都将丢失。再次调用这个函数的时候,所有的局部变量,堆栈信息都将重新创建,跟之前的调用再无关系。

有时候我们并不希望函数只返回一个值,而是希望返回一个序列。要做到这一点,这种函数需要能够保存自己的工作状态。这样的话,就不能使用我们通常所使用的return语句,因为一旦使用return语句,代码执行的控制权就交给了函数被调用的地方,函数的所有状态将被清零。在这种情况下,我们就需要使用yield关键字。含有yield关键字的地方,就是一个生成器。

在python中,生成器通过生成器函数生成,生成器函数定义的方法很普通函数定义的方法一致。唯一不同的地方就是,生成器函数不用return返回,而是用yield关键字一次返回一个结果,在每个结果之间挂起与继续他们的状态,来自动实现迭代(循环)

具体的例子:

>>> def foo():
...    print "begin"
...    for i in range(3):
...        print "before yield", i
...        yield i
...        print "after yield", i
...    print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0
0
>>> f.next()
after yield 0
before yield 1
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

注:生成器表达式

生成器表达式时列表推导式的生成器版本,看起来像列表推导式,但是它返回的是一个生成器对象而不是列表对象。把列表生成式中的中括号[]改为圆括号(),就是生成器表达式。

In [1]: a = [i * i for i in range(1,11)]

In [2]: b = (i * i for i in range(1,11))

In [3]: a
Out[3]: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

In [4]: b
Out[4]: <generator object <genexpr> at 0x000002BC8E2A58E0>

总结

1.关于可迭代对象

  • 一类是集合数据类型,如list、tuple、dict、set、str等;
  • 一类是generator,包括生成器表达式和带yield的generator function
  • 这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

2.关于可迭代对象和迭代器

  • 凡是可作用于for循环的对象都是iterable类型

  • 凡是可作用于next()函数或者__next()__方法的对象都是Iterator类型,它们表示一个惰性计算的序列;

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

  • 目的是在使用迭代器的时候,减少内存的占用。

3.生成器

  • 生成器是一种特殊的迭代器,使用关键字yield来获取一次结果。


转载:HeatonHsu

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值