举例讲解Python中的迭代器、生成器与列表解析用法
|
迭代器:初探 上一章曾经提到过,其实for循环是可用于任何可迭代的对象上的。实际上,对Python中所有会从左至右扫描对象的迭代工具而言都是如此,这些迭代工具包括了for循环、列表解析、in成员关系测试以及map内置函数等。 “可迭代对象”的概念在Python中是相当新颖的,基本这就是序列观念的通用化:如果对象时实际保存的序列,或者可以再迭代工具环境中一次产生一个结果的对象,那就看做是可迭代的。 >>文件迭代器 这个接口就是Python中所谓的迭代协议:有__next__方法的对象会前进到下一个结果,而在一系列结果的末尾时,则会引发StopIteration。任何这类对象都认为是可迭代的。任何这类对象也能以for循环或其他迭代工具遍历,因为所有迭代工具内部工作起来都是在每次迭代中调用__next__,并且捕捉StopIteratin异常来确定何时离开。
for line in open('script.py'):
print(line.upper(),end='')
上面的代码就是文件迭代的一个例子,并且这种用法是最高效的文件读取方法,主要有三个优点:这是最简单的写法,运行快,并且从内存使用情况来说也是最好的。 替代的写法是:
for line in open('script.py').readlines():
print(line.upper(),end='')
这种调用方法会把文件一次性读到内存中,如果文件太大,那么内存会被消耗光的。 >>手动迭代:iter和next 从技术角度来讲,迭代协议还有一点值得注意。当for循环开始时,会通过它传给iter内置函数,以便从可迭代对象中获得一个迭代器,返回的对象含有需要的next方法。调用iter的步骤对于文件来说不是必须的,因为文件对象就是自己的迭代器,但是对于其他的一些内置数据类型来说,就不一定了。 列表以及很多其他的内置对象,不是自身的迭代器,因为它们支持多次打开迭代器。对这样的对象,我们必须调用iter来启动迭代: L=[1,2,3] iter(L) is L #return false L.__next__() #会报错 I = iter(L) I.__next__() I.__next__() 虽然Python迭代工具自动调用这些(iter,__next__)函数,我们也可以使用它们来手动地应用迭代协议。 列表解析:初探 >>列表解析基础知识 L=[1,3,4,5] L = [x+10 for x in L] 列表解析写在一个方括号中,因为它们最终是构建一个新的列表的一种方式。它们以我们所组成的一个任意的表达式开始,该表达式使用我们所组成的一个循环变量(x+10)。这后面跟着我们现在应该看作是一个for循环头部的部分,它申明了循环变量,以及一个可迭代对象(for x in L) 要运行该表达式,Python在解释器内部执行一个遍历L的迭代,按照顺序把x赋给每个元素,并且收集对各元素运行左边的表达式的结果。我们得到的结果列表就是列表解析所表达的内容――针对L中的每个x,包含了x+10的一个新列表。 其实列表解析式并不是必须的,因为它能完成的工作都能够通过for循环完成,但是列表解析式比手动的for循环语句运行得更快(往往速度快一倍),因为它们的迭代在解释器内部是以C语言的速度执行的,而不是以手动的Python代码执行的,特别是对于较大的数据集合,这是使用列表解析的一个主要的性能优点。 当我们考虑在一个序列中的每个项上执行一个操作时,都可以考虑使用列表解析。 >>扩展的列表解析语法
lines = [line.rstrip() for line in open('script.py') if line[0]='p']
这条if子句检查从文件读取的每一行,看它的第一个字符是否是p;如果不是,从结果列表中省略改行。 事实上,如果我们愿意的话,列表解析可以变得更加复杂――它们的完整语法允许任意数目的for子句,每个子句有一个可选的相关的if子句。 Python3.0中的新的可迭代对象 Pyton3.0中的一个基本的改变是,它比Python2.x更强调迭代。除了与文件和字典这样的内置类型相关的迭代,字典方法keys、values和items都在Python3.0中返回可迭代对象。 返回一个可迭代对象而不是返回一个结果列表的好处在于节省了内存的空间。 >>多个迭代器VS单个迭代器 >>字典视图迭代器 和所有迭代器一样,我们总可以通过把一个Python3.0字典视图传递到list内置函数中,从而强制构建一个真正的列表。然而,这通常不是必须的。 此外,Python3.0字典仍然有自己的迭代器,它返回连续的键。因此,无需直接在此环境中调用keys: for key in D:print(key,end='') >>列表解析与map #循环的方法 res=[] for x in 'spam': res.append(ord(x)) #map函数的方法 res=list(map(ord,'spam')) #列表解析 res=[ord(x) for x in 'spam'] >>增加测试和嵌套循环 #列表解析 [x ** 2 for x in range(10) if x % 2 == 0] #map list(map((lambda x:x**2),filter((lambda x: x % 2==0),range(10)))) 上述的两行代码都是搜集了0~9中的偶数的平方和,可以很明显的看到,完成同样的功能,列表解析的语句简单地多。 实际上,列表解析还能够更加通用。你可以在一个列表解析中编写任意数量的嵌套的for循环,并且每一个都有可选的关联的if测试。通用结构如下所示:
expression for target1 in iterable1 [if comdition1]
for target2 in iterable2 [if condition2] ...
for targetN in iterableN [if conditionN]
当for分句嵌套在列表解析中时,它们工作起来就像等效的嵌套的for循环语句。例如,如下的代码: res=[x+y for x in [0,1,2] for y in [100,200,300]] 与下面如此冗长的代码有相同的效果: res=[] for x in [0,2]: for y in [100,30]: res.append(x+y) >>列表解析和矩阵 M=[[1,3],[4,5,6],[7,8,9]] N=[[2,2],[3,4]] 列表解析也是处理这样结构的强大工具,它们能够自动扫描行和列。 取出第二列的所有元素: [row[1] for row in M] #[2,8] [M[row][1] for row in (0,2)] #[2,8] 取出对角线上的元素: [M[i][i] for i in range(len(M))] #[1,9] 混合多个矩阵,下面的代码创建了一个单层的列表,其中包含了矩阵对元素的乘积。 复制代码 代码如下: [M[row][col] * N[row][col] for row in range(3) for col in range(3)] #[2,6,12,15,18,28,32,36] 下面的代码再复杂一点,构造一个嵌套的列表,其中的值与上面的一样: 复制代码 代码如下: [[M[row][col] * N[row][col] for col in range(3)] for row in range(3)] #[[2,[12,18],[28,36]] res=[] for row in range(3): tmp=[] for col in range(3): tmp.append(M[row][col]*N[row][col]) res.append(tmp) >>理解列表解析 重访迭代器:生成器 如今的Python对延迟提供更多的支持――它提供了工具在需要的时候才产生结果,而不是立即产生结果。特别地,有两种语言结构尽可能地拖延结果创建。 生成器函数:编写为常规的def语句,但是是使用yield语句一次返回一个结果,在每个结果之间挂起和继续它们的状态。 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
