python迭代器和生成器
- 迭代是访问集合元素的一种方式。
- 迭代器是一个表示数据流的对象,它可以记住遍历的位置,每次返回数据的下一个元素。
- 迭代器从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
1. 容器、可迭代对象
在python中一切皆对象,对象的抽象就是类,而对象的集合就是容器。
列表:[, ]
元组:(, )
字典: {0:0, }
集合 ( )
以上都是容器。对于容器,可以想象成多个元素在一起的单元;而不同容器的区别,正是内部数据结构的实现方法。然后就可以针对不同场景,选择不同时间和空间复杂度的容器。
所有的容器都是可迭代的(iterable)。这里的迭代,和枚举不完全一样。
迭代可以想象成是去买苹果,卖家并不告诉你他又多少库存。这样每次你都需要告诉卖家,你要一个苹果,然后卖家采取行为:要么给你一个苹果;要么告诉你,苹果已经卖完了。你并不需要知道,卖家在仓库是怎么摆放苹果的。
如何判断一个对象是否可迭代。(还有另一种方法, 是 isinstance(obj. lterable))。
def is_iterable(param):
try:
iter(param)
return True
except TypeError:
return False
params = [
1234,
'1234',
[1, 2, 3, 4],
set([1, 2, 3, 4]),
{1:1, 2:2, 3:3, 4:4},
(1, 2, 3, 4)
]
for param in params:
print('{} is iterable? {}'.format(param, is_iterable(param)))
########## 输出 ##########
1234 is iterable? False
1234 is iterable? True
[1, 2, 3, 4] is iterable? True
{1, 2, 3, 4} is iterable? True
{1: 1, 2: 2, 3: 3, 4: 4} is iterable? True
(1, 2, 3, 4) is iterable? True
2. 可迭代对象的本质
我们分析对可迭代对象进行迭代的过程,发现每迭代一次(即在for...in...中每循环一次)都会返回对象中的下一条数据,一直向后读取数据直到迭代了所有数据后结束。
那么,在这个过程中就应该有一个“人”去记录每次访问到了第几条数据,以便每次迭代都可以返回下一条数据。我们把这个能帮助我们进行数据迭代的“人”称为迭代器(Iterator)。
可迭代对象的本质就是可以向我们提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用。
迭代器帮助我们进行数据的迭代,而可迭代对象,通过__iter__ 方法向我们提供一个迭代器,再通过__next__方法就可以实现遍历。for in 语句将这个过程隐式化,所以只需要知道大概做了什么就行了。
一个具备了 _iter_ 方法的对象就是一个可迭代对象
>>> class MyList(object):
... def __init__(self):
... self.container = []
... def add(self, item):
... self.container.append(item)
... def __iter__(self):
... """返回一个迭代器"""
... # 暂时忽略如何构造一个迭代器对象
... pass
...
>>> mylist = MyList()
>>> from collections import Iterable
>>> isinstance(mylist, Iterable)
True
>>>
# 这次发现添加了__iter__方法的mylist对象已经是一个可迭代对象了
3.iter() 函数和 next() 函数
list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。iter()函数实际上就是调用了可迭代对象的__iter__方法。
>>> li = [11, 22, 33]
>>> li_iter = iter(li)
>>> next(li_iter)
11
>>> next(li_iter)
22
>>> next(li_iter)
33
>>> next(li_iter)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
4.迭代器
通过上面的分析,迭代器是用来帮助我们记录每次迭代访问到的位置,当我们对迭代器使用next()函数的时候,迭代器会向我们返回它所记录位置的下一个位置的数据。实际上,在使用next()函数的时候,调用的就是迭代器对象的__next__方法(Python3中是对象的__next__方法,Python2中是对象的next()方法)。所以,我们要想构造一个迭代器,就要实现它的__next__方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身即可。
一个实现了__iter__方法和__next__方法的对象,就是迭代器。
class MyList(object):
"""自定义的一个可迭代对象"""
def __init__(self):
self.items = []
def add(self, val):
self.items.append(val)
def __iter__(self):
myiterator = MyIterator(self)
return myiterator
class MyIterator(object):
"""自定义的供上面可迭代对象使用的一个迭代器"""
def __init__(self, mylist):
self.mylist = mylist
# current用来记录当前访问到的位置
self.current = 0
def __next__(self):
if self.current < len(self.mylist.items):
item = self.mylist.items[self.current]
self.current += 1
return item
else:
raise StopIteration
def __iter__(self):
return self
if __name__ == '__main__':
mylist = MyList()
mylist.add(1)
mylist.add(2)
mylist.add(3)
mylist.add(4)
mylist.add(5)
for num in mylist:
print(num)
5.生成器
生成器是懒人版本的迭代器
利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。
但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更简便的语法,即生成器(generator)。
常规的函数计算一个值并返回,但是生成器会返回返回值的迭代器。
声明一个迭代器很简单,[i for i in range(100000000)] 就可以生成一个包含一个一亿元素的列表,每个元素生成后都会保存在内存中,不过不需要在内存中同时保存这么多东西,比如对元素求和,只需要知道在相加那一刻是多少就行了,用完就可以释放了。
于是 就出现了生成器,在你调用 __next__ 方法是 才会生成下一个变量,生成器在python的写法使用小括号括起来, (i for i in range(10000000)) ,即初始化了一个生成器
生成器被认为是可恢复执行的函数。
def generate_ints(N):
for i in range(N):
yield i
任何含有yield关键字的函数都被认为是生成器函数。
当生成器函数被调用时,它不会返回单个值,而是会返回支持迭代器协议的生成器对象。
在执行yield语句时,生成器输出i的值,这和return语句类似。yield和return语句之间的最大区别在于,在到达yield时,生成器的执行状态将被暂停,并保留局部变量。在下一次调用生成器的.next()方法时,该函数将继续执行。
下面是generate_ints()生成器的使用方法:
>>> gen = generate_ints(3)
>>> gen
<generator object generate_ints at 0x10dfe2a50>
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
写成for i in generate_ints(3)或者a,b,c=generate_ints(3)也是一样的。

浙公网安备 33010602011771号