一. 创造器(creator)
这是我自己造的一个名词,因为在python术语中,对只实现了__next__()方法的对象,好像没有任何名分,为了说明,我将只实现了__next__()方法的对象称为创造器(creator)。
class O_Next:
def __init__(self):
self.index = -1
def __next__(self):
self.index += 1
return self.index
o_next = O_Next()
import collections
print(isinstance(o_next, collections.Iterable))
print(isinstance(o_next, collections.Iterator))
>>> False
>>> False
通过Python 的类型检查,可以看到,创造器即没有可迭代性,又不是python认可的迭代器,是一个无名英雄,还好我给它起了个名字来识别,创造器。
用标准函数 next(), 每调用一次创造器对象,将运行一次对象内部的__next__()方法;外部调用程序可决定需要调用多少次。
result = [next(o_next) for i in range(5)]
print(result)
result = [next(o_next) for i in range(5)]
print(result)
>>> [0, 1, 2, 3, 4]
>>> [5, 6, 7, 8, 9]
若用 while True 进行调用,对象将会被无限循环地调用下去;可在对象内部增加停止迭代的条件判断,通过触发 StopIteration异常,让调用程序捕获后,来终止迭代处理。
class O_Next2():
def __init__(self):
self.index = -1
def __next__(self):
if self.index > 5 - 1:
raise StopIteration
else:
self.index += 1
return self.index
o_next2 = O_Next2()
while True:
try:
print(next(o_next2))
except StopIteration as e:
break
>>> 0
>>> 1
>>> 2
>>> 3
>>> 4
>>> 5
二. 可迭代对象(Iterable)
具有可迭代性的对象是内部只需要实现了 __iter__()方法的对象,该方法必须返回一个实现了 __next__()方法的创造器对象(官方认为必须返回的是迭代器对象,其实不然),否则无法被外部程序用for, list()等方法正常调用。比如: list(), set(), tuple()等,我们可以看看常见容器类型对象的可迭代性,是否为迭代器,及__next__()返回的迭代器对象
import collections
print(isinstance(list(), collections.Iterable), isinstance(list(), collections.Iterator), type(iter(list())))
print(isinstance(set(), collections.Iterable), isinstance(set(), collections.Iterator), type(iter(set())))
print(isinstance(dict(), collections.Iterable), isinstance(dict(), collections.Iterator), type(iter(dict())))
print(isinstance(tuple(), collections.Iterable), isinstance(tuple(), collections.Iterator), type(iter(tuple())))
>>> True False <class 'list_iterator'>
>>> True False <class 'set_iterator'>
>>> True False <class 'dict_keyiterator'>
>>> True False <class 'tuple_iterator'>
可见,常见容器对象具有可迭代性,但不是迭代器,它们有各自专用的迭代器。
接下来,我们自定义一个可迭代的对象,用自定义的创造器(不是迭代器)。
class O_Iter():
def __iter__(self):
return O_Next2()
o_iter = O_Iter()
print(isinstance(o_iter, collections.Iterable), isinstance(o_iter, collections.Iterator), type(iter(o_iter)))
>>> True False <class '__main__.O_Next2'>
用for, list(),调用可迭代对象,将返回一个创造器对象实例, 然后无限循环调用该创造器对象的 __next__()方法,并自动处理 StopIteration 异常以终止迭代处理。
x = [i for i in o_iter]
print(x)
print(list(o_iter))
>>> [0, 1, 2, 3, 4, 5]
>>> [0, 1, 2, 3, 4, 5]
三. 迭代器(Iterator)
迭代器(Iterator)是内部同时实现了__iter__()方法和__next__()方法的对象。
__next__()方法实现创造器逻辑,__iter__()方法提供可迭代性,并返回对象自身,或其它创造器。
在python的世界里,认为实现了__next__()方法的对象,基本都会同时实现__iter__()方法,返回自身,变成迭代器。因此,没有了只实现__next__()方法的创造器的位置。
class O_Iterator():
def __init__(self):
self.index = -1
def __next__(self):
if self.index > 5 - 1:
raise StopIteration
else:
self.index += 1
return self.index
def __iter__(self):
return O_Iterator()
o_iterator = O_Iterator()
print(isinstance(o_iterator, collections.Iterable), isinstance(o_iterator, collections.Iterator), type(iter(o_iterator)))
>>> True True <class '__main__.O_Iterator'>
迭代器本身也具有可迭代性
x = [i for i in o_iterator]
print(x)
print(list(o_iterator))
>>> [0, 1, 2, 3, 4, 5]
>>> [0, 1, 2, 3, 4, 5]
四. 生成器(generator)
用更优雅简洁的方式来同时实现__iter__(), __next__(), 并自动触发StopIteration 异常
1. 生成器函数
生成器函数的实例调用,具有可迭代性,而且是迭代器
import collections
def o_generator(n):
index = 0
while index < n:
yield index
index += 1
print(isinstance(o_generator(5), collections.Iterable), isinstance(o_generator(5), collections.Iterator), type(iter(o_generator(5))))
>>> True True <class 'generator'>
x = [i for i in o_generator(5)]
print(x)
print(list(o_generator(5)))
>>> [0, 1, 2, 3, 4]
>>> [0, 1, 2, 3, 4]
2. 生成器表达式
列表生成器不加方括[ ],而是用圆括号( ),就变成了一个生成器对象(generator)
o_gen2 = (i*i for i in range(5))
print(isinstance(o_gen2, collections.Iterable), isinstance(o_gen2, collections.Iterator), type(iter(o_gen2)))
>>> True True <class 'generator'>
可迭代处理
o_gen2 = (i*i for i in range(5))
x = [i for i in o_gen2]
print(x)
print(list(o_gen2))
>>> [0, 1, 4, 9, 16]
>>> []
为什么第2次 print()输出的为空???
对同一个迭代器对象实例,全部遍历完后,就结束了,不会自己从头再来。 仔细分析观察其他的代码,每次都是重新生成了一个迭代器对象实例(创造器对象实例),初始化后,从头开始遍历的。我们可以把生成器函数的例子改写一下看看,结果将一样。
def o_generator(n):
index = 0
while index < n:
yield index
index += 1
o_gen = o_generator(5)
x = [i for i in o_gen]
print(x)
print(list(o_gen))
>>> [0, 1, 2, 3, 4]
>>> []