1、容器迭代器iterator
以list容器(tuple,range,file类似)为例,list容器可以调用自己的__iter__()方法获取用来迭代自己内部元素的迭代器。例如:
# list容器
myList = [1, 2, 3]
# 获取list容器的迭代器
it = myList.__iter__()
示例中首先给出了一个myList容器对象,内部存放了3个整数,该容器对象通过__iter__()方法获取了一个迭代器it。下面我们就可使用it迭代器来迭代myList中的元素了。
2、迭代容器元素
上面获得的it迭代器中有两个方法:__next__()和__iter__()。
(1)__iter__()
__iter__()方法返回迭代器自己,也就是it,例如:
# 迭代器可以自己返回自己
that = it.__iter__()
print((it == that) and (it is that))
迭代器it又通过调用自己的方法__iter__(),获得了一个新的迭代器that,那么他们是否是一样的呢?运行上面结果,如下:
True
“==”用来判断内容是否相等,“is”用来判断对象是否相同,“and”是逻辑与操作。
(2)__next__()
__next__()方法从容器第一个元素开始,每次调用向后迭代一个元素并返回该元素,例如:
# 第一种迭代方式:迭代list容器元素
try:
s = it.__next__()
while s:
print(s)
s = it.__next__()
except StopIteration:
pass
程序运行结果:
1
2
3
示例中,it迭代器通过调用__next__()方法遍历出myList容器中的每个元素。读者注意到,在示例中,我们使用了try except语句捕获了StopIteration,原因是__next__()方法迭代完容器后,会raise StopIteration,它也是容器迭代完的标志。我们也可以使用next()方法,将迭代器it作为参数来遍历容器,如下:
# 重置迭代器
it = mylist.__iter__()
# 第二种迭代方式
try:
s = next(it)
while s:
print(s)
s = next(it)
except StopIteration:
pass
3、简化迭代
(1) for in
如果每次都按照如上方式迭代,是不是非常麻烦,Python为我们提供了for in语句简化了迭代,例如:
for element in mylist:
print(element)
输出结果:
1
2
3
使用for in语句是不是非常便利,也不用再关心StopIteration了。
(2)其他容器迭代
# list容器迭代
for element in [1, 2, 3]:
print(element)
# tuple元组迭代
for element in (1, 2, 3):
print(element)
# map映射(或称字典)迭代
for key in {'one':1, 'two':2}:
print(key)
# 字符串迭代
for char in "123":
print(char)
# 文件迭代
# open("myfile.txt")是以读模式打开一个文件
for line in open("myfile.txt"):
print(line, end='')
4、自定义容器
容器的标记是需要实现__next__()和__iter__()两个方法,如下:
class Fab(object):
'''
斐波那契数列:后一个数是前两个数的和
例如:1,1,2,3,5,8,……
'''
def __init__(self, fab_len):
'''
数列长度
:param fab_len: 数列长度
'''
self.len = fab_len
self.count, self.a, self.b = 0, 0, 1
def __iter__(self):
'''
返回迭代器自身
:return: 返回迭代器自身
'''
return self
def __next__(self):
self.r = self.b
if self.count <= self.len:
self.a, self.b = self.b, self.a + self.b
self.count += 1
return self.r
raise StopIteration
示例中,我们定义了一个Fab容器,容器中存储斐波那契数列,其中含有含有三个方法:__init__()、__iter__()和__next__()。
__init__()方法初始化Fab容器,包括容器大小,斐波那契数列的开始元素a,b。后两个方法就不用再解释了。下面我们使用这个容器迭代斐波那契数列,如下:
# 自定义容器
myContainer = Fab(10)
# 获取迭代器
it = myContainer.__iter__()
try:
s = it.__next__()
while s:
print(s, end=',')
s = it.__next__()
except StopIteration:
pass
程序运行结果:
1,1,2,3,5,8,13,21,34,55,89,
我们同样可以使用for in语句对自定义的容器遍历,如下:
for e in Fab(10):
print(e, end=',')
程序运行结果:
1,1,2,3,5,8,13,21,34,55,89,
5、生成器
我们看到,自定义的容器需要单独定义一个类,并实现__next__()和__iter__()方法后才可以使用,这貌似有些麻烦,有没有更简单的方法呢?当然有,下面我们引入Python的生成器,通过使用关键字yield重新实现斐波那契数列容器。首先,我们看下面一个例子,如下:
程序运行结果如下:
count start
while start
before yield: n= 3
i= 3
after yield: n= 3
current while end n= 2
while start
before yield: n= 2
i= 2
after yield: n= 2
current while end n= 1
while start
before yield: n= 1
i= 1
after yield: n= 1
current while end n= 0
count end
Process finished with exit code 0
该示例中使用yield关键字,根据笔者的执行步骤标记,读者应该可以理解程序的运行过程。yield有屈服和放弃之意。熟悉Java多线程编程的读者,对此并不陌生。意思是当程序运行到yield语句时,放弃count方法之后语句的运行(同时会保存数据,以便之后从该出继续执行),转而继续执行for循环,第一次for执行完,又继续回到count方法,但这次不是从头开始,而是从上一次放弃的位置继续运行。这里很有Java多线程的感觉,一个线程运行for,另一个线程运行count方法,当线程运行到yield时,该线程进入阻塞,唤醒另一个线程for执行,如此下去……。
斐波那契数列可以通过如下生成器简化:
# 代码4
def fab(len):
count, a, b = 0, 0, 1
while count < len:
yield b
a, b = b, a + b
count += 1
# 执行
for e in fab(10):
print(e,end=',')
相比于自定义容器是不是超简洁。哈哈!