可迭代对象 vs 迭代器 vs 生成器(转)

 

可迭代对象 vs 迭代器 vs 生成器(转)

来自知乎:https://www.zhihu.com/question/20829330

from:http://python.jobbole.com/86258/

在使用Python的过程中,很容易混淆如下几个关联的概念:

  • 容器(container)
  • 可迭代对象(Iterable)
  • 迭代器(Iterator)
  • 生成器(generator)
  • 生成器表达式
  • {list, set, dict} 解析式

它们之间的关系如下表所示:

容器(container)

容器是用来储存元素的一种数据结构,它支持隶属测试,容器将所有数据保存在内存中,在Python中典型的容器有:

  • list, deque, …
  • set,frozesets,…
  • dict, defaultdict, OrderedDict, Counter, …
  • tuple, namedtuple, …
  • str

容器相对来说很好理解,因为你可以把它当成生活中的箱子、房子、船等等。
一般的,通过判断一个对象是否包含某个元素来确定它是否为一个容器。例如:

print(1 in [1,2,3]  ) # lists
print(4 not in [1,2,3])
print(1 in {1,2,3}    )# sets
print(4 not in {1,2,3})
print(1 in (12,3)     ) # tuples
print(4 not in (1,2,3))

True
True
True
True
False
True

字典容器通过检查是否包含键来进行判断:

d = {1:"foo", 2:"bar", 3:"qux"}
print(1 in d)
print(4 not in d)
print("foo" not in d)
True
True
True

字符串通过检查是否包含某个子 串来判断:

s ="foobar"
print("b" in s)
print("x" not in s)
print("foo" in s)
True
True
True

 注意:并非所有的容器都是可迭代对象 

可迭代对象 

正如前面所提到的,大部分容器都是可迭代的,但是还有其他一些对象也可以迭代,例如,文件对象以及管道对象等等,容器一般来说存储的元素是有限的,同样的,可迭代对象也可以用来表示一个包含有限元素的数据结构。

可迭代对象可以为任意对象,不一定非得是基本数据结构,只要这个对象可以返回一个iterator。听起来可能有点费解,但是可迭代对象与迭代器之间有一个显著的区别。先看下面的例子: 

x=[1,2,3]
y = iter(x)
z = iter(x)
print(next(y))
print(next(y))
print(next(y))
print(next(z))
print(type(x))
print(type(y))
1
2
3
1
<class 'list'>
<class 'list_iterator'>

在这里,x是可迭代对象,而y和z都是迭代器,它们从可迭代对象x中获取值。

 注意:可迭代的类中,一般实现以下两个方法,__iter__()以及__next()__方法,__iter__()方法返回self。

当我们运行以下代码的时候:

x = [1,2,3]
for elem in x:
     ...

实际调用过程如下:

当我们反向编译这段代Python码的时候,可以发现它显示调用了 GET_ITER,本质上跟调用iter(x)一样,而FOR_ITER指令相等于调用next()方法来获取每个元素。

迭代器(Iterators)

那么什么是迭代器呢?任何具有__next__()方法的对象都是迭代器,对迭代器调用next()方法可以获取下一个值。而至于它使如何产生这个值的,跟它能否成为一个迭代器并没有关系。

所以迭代器本质上是一个产生值的工厂,每次向迭代器请求下一个值,迭代器都会进行计算出相应的值并返回。

迭代器的例子很多,例如,所有itertools模块中的函数都会返回一个迭代器,有的还可以产生无穷的序列。 

from itertools import count
counter = count(start=13)
print(next(counter))
print(next(counter))
13
14

有的函数根据有限序列中生成无限序列:

from itertools import cycle
colors = cycle(["red","white","blue"])
print(next(colors))
print(next(colors))
print(next(colors))
print(next(colors))
red
white
blue
red
white

有的函数根据无限序列中生成有限序列:

from itertools import islice
from itertools import cycle
colors = cycle(['red', 'white', 'blue'])               # infinite
limited = islice(colors, 0, 5)                         # finite
for x in limited:                                      # so safe to use for-loop on
    print(x)
red
white
blue
red
white

为了更好的理解迭代器的内部结构,我们先来定义一个生成斐波拉契数的迭代器:

 比喻:从外部来看,迭代器就像政府工作人员一样,没人找他办事的时候(请求值),工作人员就闲着,当有人来找他的时候(请求值),工作人员就会忙一会,把请求的东西找出来交给请求的人。忙完之后,又没事了,继续闲着。

 

生成器

生成器其实就是一种特殊的迭代器。它使一种更为高级、更为优雅的迭代器。
使用生成器让我们可以以一种更加简洁的语法来定义迭代器。
让我们先明确以下两点:

  • 任意生成器都是迭代器(反过来不成立)
  • 任意生成器,都是一个可以延迟创建值的工厂

下面也是一个生成斐波那契序列的工厂函数,不过是以生成器的方式编写的:

上面的代码是不是既优雅又简洁?注意其中用到的魔法关键字 

生成器的类型

在Python中两种类型的生成器:生成器函数以及生成器表达式。生成器函数就是包含yield参数的函数。生成器表达式与列表解析式类似。
假设使用如下语法创建一个列表:

numbers = [1, 2, 3, 4, 5, 6]
print([x * x for x in numbers])

使用set解析式也可以达到同样的目的: 

 

posted @ 2018-01-22 12:47  ShengLeQi  阅读(135)  评论(0)    收藏  举报