python迭代器与解析
python迭代器与解析
迭代器初探
可能大家都已经知道for循环语句可以作用域任何序列类型,包括列表、元组以及字符串。实际上for循环能够作用于任何可迭代的对象,除了for语句,python中所有会从左至右的迭代工具都是如此,这些迭代工具包括:for循环、列表解析、in成员关系测试以及map内置函数等….
这里就涉及到很重要的一个概念-----可迭代对象,除此之外还有一个与它很类似的概念,叫做迭代对象,很多人经常分不清楚他们。迭代对象是指实现了__iter__
与next
方法的对象,而可迭代对象可以只实现__iter__
方法,也可以两个都实现。有的可迭代对象的迭代对象就是它本身。说了那么多,不如我们直接自己实现一下:
class MyRange(object):
def __init__(self, n):
self.idx = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.idx < self.n:
val = self.idx
self.idx += 1
return val
else:
raise StopIteration()
上面这个类既实现了__iter__
方法,也实现了next方法,并且它的__iter__
方法返回了它自身,所以这个可迭代对象的的迭代对象就是它自身。上面的类其实就相当于我们python中使用的xrange函数。
myRange = MyRange(3)
for i in myRange:
print i
这里写图片描述
如果一个可迭代对象同时它的迭代对象又是它本身的话会导致一个问题,无法重复迭代,例如:
从图中可以看到,当我迭代完一次过后,迭代器就被迭代完了,当我第二次用for迭代的时候没有任何输出。解决这一问题的办法就是分离迭代对象与可迭代对象。
class Zrange:
def __init__(self, n):
self.n = n
def __iter__(self):
return ZrangeIterator(self.n)
class ZrangeIterator:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
zrange = Zrange(3)
print zrange is iter(zrange)
print [i for i in zrange]
print [i for i in zrange]
结果:
这里写图片描述
注:我们可以通过iter()内建函数获取一个可迭代对象的迭代对象,然后通过is比较,例如:
这里写图片描述 可以看到列表类型的迭代对象不是它本身
手动迭代:iter与next
相信通过上面的例子,大家已经很清楚迭代器的工作原理了,下面我们来通过手动迭代,更加直观的来认识迭代器。
查看大图可以看到,我们创建了一个a列表,这个列表中实现了__iter__
方法,我们已经知道这个方法返回了一个迭代对象,我们接着看一下
这里写图片描述
可以看到这个迭代对象中实现了next方法。
这里写图片描述
然后我们通过手动迭代观察了一下,每调用一次next迭代器就走一步,走到头就抛出StopIteration。我们的for循环等迭代操作也就是利用了这个原理。
注:文件也是一个可迭代对象,我们可以用for循环按行遍历
列表解析初探
我们用一个例子走近列表解析的大门,加入我们想要修改一个列表,以前我们能会这么做:
L = [1,2,3,4,5]
for i in range(len(L)):
L[i] += 10
print L
但是实际上这样太麻烦了,我们完全可以偷懒,而且还可以提高运行效率(通常列表解析效率更高):
L = [x+10 for x in L]
print L
现在让我们来更详细的剖析一下这个例子: 列表解析写在一个方括号中,因为他们最终是构建一个新的列表(python3.0中,可以用解析构造元组,字典等)。上面的列表解析式执行的操作就是,依次从原来的L列表中取出一个数,暂存在x变量里,然后这个x在加上10,直到遍历完整个L表,同时也就形成了一个新的列表。 再来看一个例子:
a = ['123n','234n']
b = [x.strip() for x in a]
print b
这里写图片描述
我们对原来列表中的每一项去掉了换行符,然后组成了新的列表
生成器
刚刚我们也了解了列表解析式是什么东西,其实生成器的写法与列表解析式差不多,只是把方括号改为圆括号。例如:
a = (x for x in range(11))
这就构造了一个生成器。
这里写图片描述
那到底生成器是什么东西?它有什么用呢? 我们通常在写列表解析式的时候,都是表达式执行过后都会直接生成一个序列,比如:
a = [x for x in range(11)]
就会生成
[1,2,3,4,5,6,7,8,9,10]
这样一个完整的序列,但是生成器表达式执行后生成的不是一个序列,而是相当于一种算法,每运行一次这个表达式都会返回序列的下一个值,这个值是现场生成的(不是一开始就保存在内存中的)。这样有什么好处,相信大家都可以想到——节约空间(也许小程序中感受不到有什么差异,但是当这个序列很大的时候就…..) 实际上,生成器生成的是一个迭代对象也是一个可迭代对象。所以,其实它也有一个next方法:
这里写图片描述
当然,这种写法构造的生成器,实际上它能完成的功能不丰富,就像一个列表解析式一样。所以,还有另外一种写法,例如:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1
上面的函数执行后会打印一串斐波那契数列。我们同样可以写成一个生成器,这只需要简单的把print改为yield。
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
有了yield之后,每调用一次next方法,就会返回yield后面的值,并且不再往后执行,而是在下一次调用next方法时执行,一直重复,直到抛出StopIteration错误。
send与close方法
这两个方法也是与生成器相关的,但是感觉用的不太多,就不详细介绍了。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 图解HTTP读书笔记
- Flutter性能调优、复杂业务保证Flutter的高性能高流畅
- requests项目实战--抓取百度热搜
- [代码片段]谷歌的一个不错的CSS样式,不得不说大厂的前端设计师美感就是好。
- 罗马数字转整数
- 找出两个文件中相同的单词(java实现)
- Apache日志变量详解
- 三、jQuery中的Ajax
- SAP ABAP ADBC和Java JDBC的使用比较
- 自己开发的一个SAP CRM订单统计工具
- SAP ABAP和Java跨域请求问题的解决方案
- 使用ABAP Channel实现一个订单跟踪工具,提高日常工作效率
- SAP CRM状态字段下拉列表里数据的填充原理
- SAP CRM订单状态管理的一些重要的数据库表
- Angular应用的依赖注入调试