python迭代器与生成器及yield
一、迭代器(itertor)
1.可迭代:
在Python中如果一个对象有__iter__()方法或__getitem__()方法,则称这个对象是可迭代的(iterable)。
其中__iter__()方法的作用是让对象可以用“for ... in...”方式来循环遍历,_getitem_()方法是让对象可以通过“实例名[index]”的方式访问实例中的元素。换句话说,两个条件只要满足一条,就可以说对象是可迭代的。
python中的可迭代对象有:
(1)序列:字符串、列表、元组
(2)非序列:字典、文件
(3)自定义类:用户自定义的类实现了__iter__()或__getitem__()方法的对象
2.迭代器:
在Python中如果一个对象有__iter__()方法和__next__()方法,则称这个对象是迭代器(Iterator);其中__iter__()方法是让对象可以用for ... in ...循环遍历,_next_()方法是让对象可以通过next(实例名)访问下一个元素。这两个方法必须同时具备,才能称之为迭代器。
列表List、元组Tuple、字典Dictionary、字符串String等数据类型虽然是可迭代的,但都不是迭代器,因为他们都没有__next__()方法。
from collections import Iterable,Iterator
# 字符串、列表、元组、集合、字典都是可迭代对象
print(isinstance('123',Iterable)) # True
print(isinstance([1,2,3],Iterable)) # True
print(isinstance((1,2,3),Iterable)) # True
print(isinstance({1,2,3},Iterable)) # True
print(isinstance({"one":1,"two":2,"three":3},Iterable)) # True
# 字符串、列表、元组、集合、字典都不是迭代器
print(isinstance('123',Iterator)) # False
print(isinstance([1,2,3],Iterator)) # False
print(isinstance((1,2,3),Iterator)) # False
print(isinstance({1,2,3},Iterator)) # False
print(isinstance({"one":1,"two":2,"three":3},Iterator)) # False
3.总结:
- 迭代器都是可迭代的,但可迭代的不一定是迭代器;可用for ... in ...循环的都是可迭代的,可用next()遍历的才是迭代器;
- next()是单向的,一次只获取一个元素,获取到最后一个元素后停止;
- 在可迭代的对象中提前存储了所有的元素,而迭代器是惰性的,只有迭代到了某个元素,该元素才会生成。
4.延迟计算或惰性求值 (Lazy evaluation)
迭代器不要求你事先准备好整个迭代过程中所有的元素。仅仅是在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合。
迭代之前元素可以是不存在的,迭代之后元素也可以被销毁,因此迭代器在处理大量数据甚至无限数据时具有加载数据快、占用内存小等优势。
5.创建迭代器:
(1)使用内建的工厂函数iter(iterable[, sentinel])
iter()函数只传入一个参数时,参数必须为可迭代对象;当使用第二个参数sentinel(哨兵)时,第一个参数必须是一个可调用对象。
当有第二个参数sentinel传入时,参数iterable应是一个可调用对象,这时候迭代器它会重复地调用第一个参数,当枚举到的值等于哨兵时,就会抛出异常StopIteration。
s = "abcdefgh"
iter1 = iter(s)
print(isinstance(iter1,Iterator)) # True
使用第二个参数的情况此处不描述了
(2)自己写类,其中实现__iter__()方法和__next__()方法
在__next__()方法中必须对迭代进行检查,超出范围则触发 StopIteration 异常。注意避免死循环。
class MyIterator(object):
def __init__(self, data):
self.data = data
def __iter__(self):
return self
def __next__(self):
if self.data <= 20:
self.data += 2
return self.data
else:
raise StopIteration
myIter = MyIterator(5)
print(isinstance(myIter,Iterator)) # True
print(next(myIter)) # 7
print(next(myIter)) # 9
print(next(myIter)) # 11
...
二、生成器(generator)
在Python中,不会一次性的生成所有的值,而是调用一次,生成一个值,再调用一次,生成下一个值。
即生成器不会把结果保存在一个系列中,而是保存生成器的状态,在每次进行迭代时返回一个值,直到遇到StopIteration异常时结束。
创建生成器的两种基本方法
1.生成器表达式:
通列表解析语法,在写列表生成式时,把[]换成()即可
实例:
# 列表生成式
>>> l = [i for i in range(5)]
>>> l
[0, 1, 2, 3, 4]
# 生成器表达式
>>> g = (i for i in range(5))
>>> g
<generator object <genexpr> at 0x000001A5108D5938>
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
2.生成器函数
在 Python 中,使用了(一个或多个)yield语句的函数,就叫做生成器函数。
解释器处理这种函数定义的时候将创建特殊的生成器函数对象,而不是普通的函数对象。调用生成器函数的时候,返回值就是一个生成器对象。它只能用于迭代操作,更简单点理解生成器就是一种迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
实例:
# 实现的斐波那契数列
def fib_gen(n):
f0, f1 = 0, 1
for n in range(n):
yield f0
f0, f1 = f1, f0+f1
print(fib_gen(5)) # <generator object fib_gen at 0x0000023A5BB78830>
g = fib_gen(6) # g就是一个生成器对象(也是一种迭代器)
print(next(g)) # 0
print(next(g)) # 1
print(next(g)) # 1
print(next(g)) # 2
# 生成器是包含有__iter__()和__next__()方法的,所以可以直接使用for来迭代
for x in fib_gen(5):
print(x, end=', ')
# 0, 1, 1, 2, 3,
2.1.yield 与 return
在一个生成器中,如果没有return,则默认执行到函数完毕时返回StopIteration异常。
>>> def gen():
... for i in range(2)
... yield 1
...
>>> g=gen()
>>> next(g) #第一次调用next(g)时,会在执行完yield语句后挂起,此时程序并没有执行结束。
0
>>> next(g) #第二次调用next(g),同样会在执行完yield语句后挂起,此时程序并没有执行结束。
1
>>> next(g) #程序试图从yield语句的下一条语句开始执行,发现已经到了结尾,所以抛出StopIteration异常。
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
生成器函数有return,如果return语句没有返回值,则直接抛出StopIteration异常。如果return语句有返回值,那么这个返回值会成为抛出StopIteration异常的一个说明。
>>> def ff():
... for i in range(2):
... yield i
... return 'i finished.'
...
>>> g = ff()
>>> next(g)
0
>>> next(g)
1
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: i finished.
2.2.yield from语法
yield from 是在Python3.3才出现的语法。所以这个特性在Python2中是没有的。
yield from 后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,甚至是生成器。
yield from 主要用于生成器的嵌套,重点是帮我们自动处理内外层之间的异常问题。
这种用法参考博客:深入理解yield from语法
3.生成器支持的方法
**① **generator.send(expr)
启动新生成器或唤醒处于挂起状态的生成器,把参数expr的值传送给它,使之成为当时挂起的那个yield表达式的值。
本操作导致generator继续执行到下一个yield表达式(或者语句),返回yield值。对新生成器调用send时参数只能是None,因为当时不存在等待值的yield表达式。
def gen():
value=0
while True:
receive=yield value
if receive=='e':
break
value = 'i got: %s' % receive
g=gen()
print(g.send(None)) # 这里用next(g)也是一样
print(g.send('aaa'))
print(g.send(3))
print(g.send('e'))
执行流程:
通过g.send(None)或者next(g)可以启动生成器函数,并执行到第一个yield语句结束的位置,yield value会输出初始值0。此时,执行完了yield语句,但是没有给receive赋值,因为执行完yield程序就处于挂起状态了,还没有来得急给receive赋值。
通过g.send('aaa'),会传入值aaa作为yield表达式的值,然后继续执行程序,此时把yield表达式的值赋值给receive,然后计算出value的值,并回到while头部,执行yield value语句又暂停。此时yield value会输出"got: aaa",然后挂起。
通过g.send(3),会重复第2步,最后输出结果为"got: 3"
当我们g.send('e')时,程序会执行break然后推出循环,最后整个函数执行完毕,所以会得到StopIteration异常。
最后的执行结果如下:
0
i got: aaa
File "生成器.py", line 104, in <module>
i got: 3
print(g.send('e'))
StopIteration
② :generator.close()
手动关闭生成器函数,如果继续调用next()获取下一个值会直接抛出StopIteration异常。
def gen():
value=0
while True:
receive=yield value
if receive=='e':
break
value = 'i got: %s' % receive
g=gen()
print(next(g))
g.close()
print(next(g)) # 继续调用抛出StopIteration异常
③ :generator.throw(type[,value[,traceback]])
启动新生成器或唤醒处于挂起状态的生成器,在当时挂起的yield表达式(语句)处抛出type类型的异常,该异常可以附带value作为参数说明,还可以有相应的traceback对象。
这个调用也返回generator生成器的下一个值,如果此时生成器退出或没有产生yield值,会抛出StopIteration异常。简单说,会抛出异常并结束程序,或者消耗掉一个yield值,或者在没有下一个yield的时候直接进行到程序的结尾。
def gen():
while True:
try:
yield 'normal value1'
yield 'normal value 2'
print('here')
except ValueError:
print('ValueError : ...... ')
except TypeError as e:
print('TypeError : ',e)
break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError,'i raised a TypeError'))
执行结果:
normal value1
Traceback (most recent call last):
ValueError : ......
normal value1
File "生成器.py", line 121, in <module>
normal value 2
print(g.throw(TypeError,'i raised a TypeError'))
TypeError : i raised a TypeError
StopIteration
python迭代器与生成器及yield的更多相关文章
- python迭代器、生成器、yield理解
简介 yield关键字是python的一种高阶用法,使用yield的函数会返回一个生成器对象,生成器又是一个迭代器,与迭代器相类似的则是可迭代对象,下面首先介绍一下迭代器吧. 迭代器 在python中 ...
- python迭代器、生成器、yield和xrange
https://blog.csdn.net/u010138758/article/details/56291013
- 【Python】迭代器、生成器、yield单线程异步并发实现详解
转自http://blog.itpub.net/29018063/viewspace-2079767 大家在学习python开发时可能经常对迭代器.生成器.yield关键字用法有所疑惑,在这篇文章将从 ...
- Python 迭代器和生成器(转)
Python 迭代器和生成器 在Python中,很多对象都是可以通过for语句来直接遍历的,例如list.string.dict等等,这些对象都可以被称为可迭代对象.至于说哪些对象是可以被迭代访问的, ...
- 一文搞懂Python迭代器和生成器
很多童鞋搞不懂python迭代器和生成器到底是什么?它们之间又有什么样的关系? 这篇文章就是要用最简单的方式让你理解Python迭代器和生成器! 1.迭代器和迭代过程 维基百科解释道: 在Python ...
- Python - 迭代器与生成器 - 第十三天
Python 迭代器与生成器 迭代器 迭代是Python最强大的功能之一,是访问集合元素的一种方式. 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问 ...
- 怎么理解Python迭代器与生成器?
怎么理解Python迭代器与生成器?在Python中,使用for ... in ... 可以对list.tuple.set和dict数据类型进行迭代,可以把所有数据都过滤出来.如下: ...
- Python:容器、迭代对象、迭代器、生成器及yield关键字
在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(generator).列表/集合/字典推导式(list, ...
- python -迭代器与生成器 以及 iterable(可迭代对象)、yield语句
我刚开始学习编程没多久,对于很多知识还完全不知道,而有些知道的也是一知半解,我想把学习到的知识记录下来,一是弥补记忆力差的毛病,二也是为了待以后知识能进一步理解透彻时再回来做一个补充. 参考链接: 完 ...
随机推荐
- Oracle执行计划学习笔记
目录 一.获取执行计划的方法 (1) explain plan for (2) set autotrace on (3) statistics_level=all (4) dbms_xplan.dis ...
- 深入学习Redis(2):持久化
前言 在上一篇文章中,介绍了Redis的内存模型,从这篇文章开始,将依次介绍Redis高可用相关的知识——持久化.复制(及读写分离).哨兵.以及集群. 本文将先说明上述几种技术分别解决了Redis高可 ...
- Access2007数据库下载地址与AccessHelper
链接:https://pan.baidu.com/s/1pLzOlTv0nqSbhzujHZht1w 提取码:1m9l AccessHelper: using System; using System ...
- 『这是一篇干货blog』
更新记录一些很好的干货博客以及工具网站. 各文章,工具网站版权归原作者所有,侵删. Articles 浅谈C++ IO优化--读优输优方法集锦 浅谈斜率优化 思维导图好助手--开心食用Xmind Ty ...
- .NET Core + Ocelot + IdentityServer4 + Consul 基础架构实现
先决条件 关于 Ocelot 针对使用 .NET 开发微服务架构或者面向服务架构提供一个统一访问系统的组件. 参考 本文将使用 Ocelot 构建统一入口的 Gateway. 关于 IdentityS ...
- javascript-发布订阅模式与观察者模式
设计模式"(Design Pattern)是针对编程中经常出现的.具有共性的问题,所提出的解决方法.著名的<设计模式>一书一共提出了23种模式. 发布订阅模式 它定义了一种对象间 ...
- Netty源码—六、tiny、small内存分配
tiny内存分配 tiny内存分配流程: 如果申请的是tiny类型,会先从tiny缓存中尝试分配,如果缓存分配成功则返回 否则从tinySubpagePools中尝试分配 如果上面没有分配成功则使用a ...
- 详解mybatis映射配置文件
一 mybatis 映射文件结构 mybatis映射配置文件存在如下顶级元素,且这些元素按照如下顺序被定义. cache – 给定命名空间的缓存配置. cache-ref – 其他命名空间缓存配置的 ...
- 读书笔记:深入理解java虚拟机(一)虚拟机的运行时的数据区域
最近在看深入了解java虚拟机第一版(周志明著),特此写读书笔记,整理其中重要的东西和自己的理解. ”java与c++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却 ...
- 开发vue但不使用vue-cli和webpack相关注意事项
1.绑定vue组件使用new Vue() 2.new Vue()需要在dom结构生成之后才有效(毕竟有需要el) 3.Vue.component注册全局组件在vue容器组件挂载之前才有效 4.当然,可 ...