Python中可迭代对象、迭代器和生成器相关
来源:互联网 发布:java程序员培训学校 编辑:程序博客网 时间:2024/06/14 18:29
Blog地址:https://www.jiangdog.com/blog/iterator-and-generator
可迭代对象 Iterable
- 可迭代对象的定义
- 如果一个对象实现了
__iter__
方法,且使用iter()
内置函数可以获取迭代器对象,那么该对象就是可迭代对象。 - 除上述之外,序列都是可迭代对象。且若一个对象实现了
__getitem__
方法,且其参数是从0开始的索引,这种对象也是可迭代对象。
- 如果一个对象实现了
- iter() 函数作用
- 尝试调用该对象的
__iter__
方法获取一个迭代器。 - 若没有实现
__iter__
方法,却实现了__getitem__
方法,Python创造一个迭代器,并有序的(从0开始)获取元素。 - 如果尝试失败,抛出
TypeError
异常。
- 尝试调用该对象的
判断是否是可迭代对象
如果该对象实现了
__iter__
方法,则其一定是可迭代对象class Foo: def __iter__(self): passprint(issubclass(Foo, abc.Iterable)) # Trueprint(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): passtry: 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) # <callable_iterator object at 0x003F4550>for v in for_one_iterator: print(v) # 3 4 5 4 5
迭代器 Iterator
迭代器含义和特点
- 标准的迭代器,需要实现
__iter__
和__next__
两个方法。 __iter__
返回迭代器本身,以便应该在使用可迭代对象的地方使用迭代器,如for循环。__next__
返回迭代器中的下一个有效元素,直到没有元素抛出StopIteration
异常。- 利用
isinstance(x, abc.Iterator)
来判断对象x是否是迭代器。 - 迭代器迭代到头耗尽,无法还原,需要重新构建迭代器。
- 迭代器
迭代器是这样的对象:实现了无参数的
__next__
方法,返回序列
中的下一个元素;如果没有元素了,那么抛出StopIteration
异常。
Python 中的迭代器还实现了__iter__
方法,因此迭代器也可以迭代。- 标准的迭代器,需要实现
迭代器和可迭代对象之间的关系
- Python从可迭代对象获取迭代器。
- 迭代器也是可迭代的,因为其实现了
__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 worderror_sentence = ErrorSentence('x y z')error_iterator = iter(error_sentence)print(error_iterator) # <__main__.ErrorSentence object at 0x000001C38F24CE10> print(isinstance(error_sentence, abc.Iterator)) # Truefor v in error_sentence: print(v) # x y z# 可迭代对象是自身的迭代器# 迭代器已耗尽,不能再次迭代for v in error_sentence: print(v)
- for循环的实现
- 判断对象是否是可迭代对象,如果不是则直接抛出
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 wordabc_sentence = Sentence('a b c')sentence_iterator = iter(abc_sentence)print(sentence_iterator) # <__main__.SentenceIterator object at 0x0000025F31C5B9B0>print(isinstance(sentence_iterator, abc.Iterator)) # Truefor 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) # <generator object Sentence.__iter__ at 0x00000249900C8308>
此时这里调用
iter()
所获得的迭代器实际上是一个实现了迭代器接口的生成器对象。
生成器 Generator
- 生成器含义和特点
- 只要Python函数的定义体中出现了
yield
关键字,则该函数就是生成器函数。调用该生成器函数返回一个生成器对象。 - 把生成器传给
next()
函数时,生成器函数会向前,执行生成器函数中下一个yield语句返回产出值并在当前位置暂停。 - 当生成器函数返回时,生成器对象抛出
StopIteration
异常。
- 只要Python函数的定义体中出现了
生成器函数和生成器表达式
普通函数和生成器函数唯一的区别就是,生成器函数在定义体中存在
yield
关键字。# 生成器函数 def gen_123(): yield 1 yield 2 yield 3 print(gen_123) # <function gen_123 at 0x00000137C8FD6158> 函数对象 print(gen_123()) # <generator object gen_123 at 0x000002366A689360> 生成器对象for v in gen_123(): print(v) # 1 2 3g = gen_123()while True: # 调用next(g)来不断获取生成器的下一个值 try: print(next(g)) except StopIteration: # 生成器函数的定义体执行完后抛出异常后终止 break
生成器表达式
生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。也就是说,如果列表推导是制造列表的工厂,那么生成器表达式就是制造生成器的工厂。
# 生成器表达式gen_ABC = (s for s in 'ABC')print(gen_ABC) # <generator object <genexpr> 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
两者的比较
接口:
- Python迭代器协议定义了两个方法:
__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>
- Python迭代器协议定义了两个方法:
- 实现方式:
- 生成器通过含有yield关键字的函数或生成器表达式来编写,这两种方式返回的生成器对象属于
types.GeneratorType
类型,且types.GeneratorType
类型的实例实现了迭代器接口,则所有的生成器都是迭代器。 - 但我们可以编写不是生成器的迭代器对象,如简单实现
__iter__
和__next__
方法,或者用C语言编写拓展。 - The type of generator-iterator objects, created by generator functions.
- 生成器通过含有yield关键字的函数或生成器表达式来编写,这两种方式返回的生成器对象属于
- 其他:
- Iterator Types
- Generator Types
参考
大部分总结参考自《Fluent Python》第五部分第14章节
生成器(generator)概念
其他
- 文中代码
- Python中可迭代对象、迭代器和生成器相关
- Python 可迭代的对象、迭代器和生成器
- Python 可迭代的对象、迭代器和生成器
- python迭代器和生成器
- Python 迭代器和生成器
- Python 迭代器和生成器
- Python迭代器和生成器
- Python迭代器和生成器
- python迭代器和生成器
- Python迭代器和生成器
- python迭代器和生成器
- python 迭代器和生成器
- python 迭代器和生成器
- Python--迭代器和生成器
- python迭代器和生成器
- Python 迭代器和生成器
- python迭代器和生成器
- python--迭代器和生成器
- Codeforces Round #441 (Div.2)
- Hadoop 在centos6.4安装ssh
- 将字符串写入文件
- 博客编辑快捷键
- alloc_large_system_hash
- Python中可迭代对象、迭代器和生成器相关
- 机器学习—经验风险最小化
- solr+tomcat
- java中不常用的关键字(个人目前见得少)
- 实验四 Linux系统管理
- 区间dp入门——tyvj1055沙子合并
- 【C语言练习】1.0
- CSS Mastery Chapter 6 需要学习的点
- mark/reset方法的使用以及导致内存溢出的原因(java)