Blog地址:https://www.jiangdog.com/blog/iterator-and-generator
__iter__
方法,且使用iter()
内置函数可以获取迭代器对象,那么该对象就是可迭代对象。__getitem__
方法,且其参数是从0开始的索引,这种对象也是可迭代对象。__iter__
方法获取一个迭代器。__iter__
方法,却实现了__getitem__
方法,Python创造一个迭代器,并有序的(从0开始)获取元素。TypeError
异常。判断是否是可迭代对象
如果该对象实现了__iter__
方法,则其一定是可迭代对象
class Foo:
def __iter__(self):
pass
print(issubclass(Foo, abc.Iterable)) # True
print(isinstance(Foo(), abc.Iterable)) # True
任何Python的序列都是可迭代的,因为他们都实现了__getitem__
方法,事实上Python中的标准序列也都实现了__iter__
方法,对__getitem__
做特殊处理是为了向后兼容,自定义序列时最好的做法是两者都实现。
对于只实现了__getitem__
方法的可迭代对象,使用isinstance()
来判断并不准确,因为iter()
函数考虑到了遗留的__getitem__
问题,而abc.Iterable
并没有。
class MyList:
def __init__(self):
self.seq = [1, 2, 3]
def __getitem__(self, index):
return self.seq[index]
def __len(self)__:
return len(self.seq)
print(isinstance(MyList(), abc.Iterable)) # False
最好的方法是在尝试迭代不可迭代对象时,会抛出TypeError
异常,此时可以try/except
捕获异常后再做进一步处理。
class MyObject:
def __init__(self):
pass
try:
iter(MyObject())
except TypeError as e:
print(e) # 'MyObject' object is not iterable
iter() 函数的另一种用法
内置iter() 函数
def iter(source, sentinel=None): # known special case of iter
"""
iter(iterable) -> iterator
iter(callable, sentinel) -> iterator
Get an iterator from an object. In the first form, the argument must
supply its own iterator, or be a sequence.
In the second form, the callable is called until it returns the sentinel.
"""
pass
第二种用法种,接收一个可调用对象和终止哨符,此时返回一个迭代器,该迭代器会不停的产出传入的可调用对象返回的值,直到出现终止哨符,则耗尽,抛出StopIteration
异常,且不产出终止哨符。
def random_for_one():
return randint(1, 6)
for_one_iterator = iter(random_for_one, 1)
print(for_one_iterator) #
for v in for_one_iterator:
print(v) # 3 4 5 4 5
迭代器含义和特点
__iter__
和__next__
两个方法。__iter__
返回迭代器本身,以便应该在使用可迭代对象的地方使用迭代器,如for循环。__next__
返回迭代器中的下一个有效元素,直到没有元素抛出StopIteration
异常。isinstance(x, abc.Iterator)
来判断对象x是否是迭代器。迭代器是这样的对象:实现了无参数的
__next__
方法,返回序列
中的下一个元素;如果没有元素了,那么抛出StopIteration
异常。
Python 中的迭代器还实现了__iter__
方法,因此迭代器也可以迭代。
迭代器和可迭代对象之间的关系
__iter__
方法,且返回迭代器自身。__next__
方法。为了支持多种遍历,如for
,拆包等,必须一个可迭代实例中获取多个独立迭代器,且迭代器能够维护自身的状态,正确的做法是对可迭代对象上调用__iter()
方法,每次都构建一个独立的新的迭代器;而不是在可迭代对象中实现__next__
方法,使其是自身的迭代器,若这样做,其在一次迭代耗尽后将无法构建新的迭代器。
# no __next__ in Iterable
class ErrorSentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration
self.index += 1
return word
error_sentence = ErrorSentence('x y z')
error_iterator = iter(error_sentence)
print(error_iterator) # <__main__.ErrorSentence object at 0x000001C38F24CE10>
print(isinstance(error_sentence, abc.Iterator)) # True
for v in error_sentence:
print(v) # x y z
# 可迭代对象是自身的迭代器
# 迭代器已耗尽,不能再次迭代
for v in error_sentence:
print(v)
TypeError
。iter(iterable)
方法,调用其__iter__
方法返回一个迭代器;若该对象未实现__iter__
接口,但存在__getitem__
方法,Python会创造一个迭代器。next(iterator)
,执行迭代器的__next__
方法,使其按序的返回下一个可用的值。StopIteration
异常,Python语言内部将会处理这个异常(除了for
之外,其他迭代上下文,如列表推导式、元组拆包等也会处理该异常)。定义一个简单的迭代器
由上述可知一个标准的迭代器需要实现__iter__
和__next__
方法,展示相关示例。
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.find('text')
def __iter__(self):
return SentenceIterator(self.words)
class SentenceIterator:
def __init__(self, words):
self.words = words
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration
self.index += 1
return word
abc_sentence = Sentence('a b c')
sentence_iterator = iter(abc_sentence)
print(sentence_iterator) # <__main__.SentenceIterator object at 0x0000025F31C5B9B0>
print(isinstance(sentence_iterator, abc.Iterator)) # True
for v in abc_sentence:
print(v) # a b c
使用生成器函数代替SentenceIterator
迭代器类:
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __iter__(self):
for word in self.words:
yield word
sentence_iterator = iter(Sentence('a b c'))
print(sentence_iterator) #
此时这里调用iter()
所获得的迭代器实际上是一个实现了迭代器接口的生成器对象。
yield
关键字,则该函数就是生成器函数。调用该生成器函数返回一个生成器对象。next()
函数时,生成器函数会向前,执行生成器函数中下一个yield语句返回产出值并在当前位置暂停。StopIteration
异常。生成器函数和生成器表达式
普通函数和生成器函数唯一的区别就是,生成器函数在定义体中存在yield
关键字。
# 生成器函数
def gen_123():
yield 1
yield 2
yield 3
print(gen_123) # 函数对象
print(gen_123()) # 生成器对象
for v in gen_123():
print(v) # 1 2 3
g = gen_123()
while True:
# 调用next(g)来不断获取生成器的下一个值
try:
print(next(g))
except StopIteration:
# 生成器函数的定义体执行完后抛出异常后终止
break
生成器表达式
生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。也就是说,如果列表推导是制造列表的工厂,那么生成器表达式就是制造生成器的工厂。
# 生成器表达式
gen_ABC = (s for s in 'ABC')
print(gen_ABC) # at 0x000001F4038E9410>
此时通过该生成器表达式构建了一个生成器对象。
标准库中的生成器函数
itertools
、functools
以及内置方法中有很多生成器函数。filter
、map
、enumerate
、all
、any
、reversed
等等。itertools
中提供了很多过滤相关、映射相关(itertools.accumulate(it, [func])
)、合并多个迭代对象相关(itertools.product()
求笛卡儿积、itertools.zip_longest()
)、输入的各个元素扩展成多个输出元素的生成器函数(itertools.combinations(it, out_len)
组合, itertools.combinations_with_replacement(it,
out_len)
包含相同元素的组合)。生成器对象的其他方法和功能
generator.__next__()
只允许客户从生成器中获取数据,generator.send(value)
允许客户代码和生成器之间双向交换数据。
# generator.send(value)
def gen():
print('start')
value = 0
while True:
receive = yield value
print(receive)
if receive == 'end':
break
value += 1
g = gen()
# 或next(g) 预激生成器
print(g.send(None)) # 0
print(g.send('a')) # 1
print(g.send('b')) # 2
try:
print(g.send('end'))
except StopIteration:
print('end')
generator.close()
能够关闭生成器,关闭生成器后继续调用next()
方法后将会抛出异常。
# generator.close()
def gen():
yield 1
yield 2
yield 3
g = gen()
print(next(g))
print(next(g))
g.close()
try:
print(next(g))
except StopIteration:
pass
接口:
__iter__
和__next__
。生成器对象实现了这两个方法,因此,所有生成器都是迭代器。
g = (i for i in 'abc')
print(g.__iter__) # <method-wrapper '__iter__' of generator object at 0x02ACAF00>
print(g.__next__) # <method-wrapper '__next__' of generator object at 0x02ACAF00>
types.GeneratorType
类型,且types.GeneratorType
类型的实例实现了迭代器接口,则所有的生成器都是迭代器。__iter__
和__next__
方法,或者用C语言编写拓展。大部分总结参考自《Fluent Python》第五部分第14章节
生成器(generator)概念