Python高级特性(1):Iterators、Generators和itertools(参考)
对数学家来说,Python这门语言有着很多吸引他们的地方。举几个例子:对于tuple、lists以及sets等容器的支持,使用与传统数学类 似的符号标记方式,还有列表推导式这样与数学中集合推导式和集的结构式(set-builder notation)很相似的语法结构。
另外一些很吸引数学爱好者的特性是Python中的iterator(迭代器)、generator(生成器)以及相关的itertools包。这 些工具帮助人们能够很轻松的写出处理诸如无穷序列(infinite sequence)、随机过程(stochastic processes)、递推关系(recurrence relations)以及组合结构(combinatorial structures)等数学对象的优雅代码。本文将涵盖我关于迭代器和生成器的一些笔记,并且有一些我在学习过程中积累的相关经验。
Iterators
迭代器(Iterator)是一个可以对集合进行迭代访问的对象。通过这种方式不需要将集合全部载入内存中,也正因如此,这种集合元素几乎可以是无限的。你可以在Python官方文档的“迭代器类型(Iterator Type)”部分找到相关文档。
让我们对定义的描述再准确些,如果一个对象定义了__iter__方法,并且此方法需要返回一个迭代器,那么这个对象就是可迭代的 (iterable)。而迭代器是指实现了__iter__以及next(在Python 3中为__next__)两个方法的对象,前者返回一个迭代器对象,而后者返回迭代过程的下一个集合元素。据我所知,迭代器总是在__iter__方法中 简单的返回自己(self),因为它们正是自己的迭代器。
一般来说,你应该避免直接调用__iter__以及next方法。而应该使用for或是列表推导式(list comprehension),这样的话Python能够自动为你调用这两个方法。如果你需要手动调用它们,请使用Python的内建函数iter以及 next,并且把目标迭代器对象或是集合对象当做参数传递给它们。举个例子,如果c是一个可迭代对象,那么你可以使用iter(c)来访问,而不是 c.__iter__(),类似的,如果a是一个迭代器对象,那么请使用next(a)而不是a.next()来访问下一个元素。与之相类似的还有len 的用法。
说到len,值得注意的是对迭代器而言没必要去纠结length的定义。所以它们通常不会去实现__len__方法。如果你需要计算容器的长度,那么必须得手动计算,或者使用sum。本文末,在itertools模块之后会给出一个例子。
有一些可迭代对象并不是迭代器,而是使用其他对象作为迭代器。举个例子,list对象是一个可迭代对象,但并不是一个迭代器(它实现了 __iter__但并未实现next)。通过下面的例子你可以看到list是如何使用迭代器listiterator的。同时值得注意的是list很好地 定义了length属性,而listiterator却没有。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
>>> a = [ 1 , 2 ] >>> type (a) < type 'list' > >>> type ( iter (a)) < type 'listiterator' > >>> it = iter (a) >>> next (it) 1 >>> next (it) 2 >>> next (it) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> StopIteration >>> len (a) 2 >>> len (it) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> TypeError: object of type 'listiterator' has no len () |
当迭代结束却仍然被继续迭代访问时,Python解释器会抛出StopIteration异常。然而,前述中提到迭代器可以迭代一个无穷集合,所以对于这种迭代器就必须由用户负责确保不会造成无限循环的情况,请看下面的例子:
1
2
3
4
5
6
7
8
9
10
|
class count_iterator( object ): n = 0 def __iter__( self ): return self def next ( self ): y = self .n self .n + = 1 return y |
下面是例子,注意最后一行试图将一个迭代器对象转为list,这将导致一个无限循环,因为这种迭代器对象将不会停止。
1
2
3
4
5
6
7
8
9
10
|
>>> counter = count_iterator() >>> next (counter) 0 >>> next (counter) 1 >>> next (counter) 2 >>> next (counter) 3 >>> list (counter) # This will result in an infinite loop! |
最后,我们将修改以上的程序:如果一个对象没有__iter__方法但定义了__getitem__方法,那么这个对象仍然是可迭代的。在这种情况 下,当Python的内建函数iter将会返回一个对应此对象的迭代器类型,并使用__getitem__方法遍历list的所有元素。如果 StopIteration或IndexError异常被抛出,则迭代停止。让我们看看以下的例子:
1
2
3
4
5
6
|
class SimpleList( object ): def __init__( self , * items): self .items = items def __getitem__( self , i): return self .items[i] |
用法在此:
1
2
3
4
5
6
7
8
9
10
11
12
|
>>> a = SimpleList( 1 , 2 , 3 ) >>> it = iter (a) >>> next (it) 1 >>> next (it) 2 >>> next (it) 3 >>> next (it) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> StopIteration |
现在来看一个更有趣的例子:根据初始条件使用迭代器生成Hofstadter Q序列。 Hofstadter在他的著作《Gödel, Escher, Bach: An Eternal Golden Braid》中首次提到了这个嵌套的序列,并且自那时候开始关于证明这个序列对所有n都成立的问题就开始了。以下的代码使用一个迭代器来生成给定n的 Hofstadter序列,定义如下:
Q(n)=Q(n-Q(n-1))+Q(n−Q(n−2))
给定一个初始条件,举个例子,qsequence([1, 1])将会生成H序列。我们使用StopIteration异常来指示序列不能够继续生成了,因为需要一个合法的下标索引来生成下一个元素。例如如果初始条件是[1,2],那么序列生成将立即停止。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class qsequence( object ): def __init__( self , s): self .s = s[:] def next ( self ): try : q = self .s[ - self .s[ - 1 ]] + self .s[ - self .s[ - 2 ]] self .s.append(q) return q except IndexError: raise StopIteration() def __iter__( self ): return self def current_state( self ): return self .s |
用法在此:
1
2
3
4
5
6
7
|
>>> Q = qsequence([ 1 , 1 ]) >>> next (Q) 2 >>> next (Q) 3 >>> [ next (Q) for __ in xrange ( 10 )] [ 3 , 4 , 5 , 5 , 6 , 6 , 6 , 8 , 8 , 8 ] |
Generators
生成器(Generator)是一种用更简单的函数表达式定义的生成器。说的更具体一些,在生成器内部会用到yield表达式。生成器不会使用
return返回值,而当需要时使用yield表达式返回结果。Python的内在机制能够帮助记住当前生成器的上下文,也就是当前的控制流和局部变量的
值等。每次生成器被调用都适用yield返回迭代过程中的下一个值。__iter__方法是默认实现的,意味着任何能够使用迭代器的地方都能够使用生成
器。下面这个例子实现的功能同上面迭代器的例子一样,不过代码更紧凑,可读性更强。
1
2
3
4
5
|
def count_generator(): n = 0 while True : yield n n + = 1 |
来看看用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
>>> counter = count_generator() >>> counter <generator object count_generator at 0x106bf1aa0 > >>> next (counter) 0 >>> next (counter) 1 >>> iter (counter) <generator object count_generator at 0x106bf1aa0 > >>> iter (counter) is counter True >>> type (counter) < type 'generator' > |
现在让我们尝试用生成器来实现Hofstadter’s
Q队列。这个实现很简单,不过我们却不能实现前的类似于current_state那样的函数了。因为据我所知,不可能在外部直接访问生成器内部的变量状
态,因此如current_state这样的函数就不可能实现了(虽然有诸如gi_frame.f_locals这样的数据结构可以做到,但是这毕竟是
CPython的特殊实现,并不是这门语言的标准部分,所以并不推荐使用)。如果需要访问内部变量,一个可能的方法是通过yield返回所有的结果,我会
把这个问题留作练习。
1
2
3
4
5
6
7
8
9
|
def hofstadter_generator(s): a = s[:] while True : try : q = a[ - a[ - 1 ]] + a[ - a[ - 2 ]] a.append(q) yield q except IndexError: return |
请注意,在生成器迭代过程的结尾有一个简单的return语句,但并没有返回任何数据。从内部来说,这将抛出一个StopIteration异常。
剩下部分略过,参考:http://blog.jobbole.com/66097/
Python高级特性(1):Iterators、Generators和itertools(参考)的更多相关文章
- Python高级特性(1):Iterators、Generators和itertools(转)
译文:Python高级特性(1):Iterators.Generators和itertools [译注]:作为一门动态脚本语言,Python 对编程初学者而言很友好,丰富的第三方库能够给使用者带来很大 ...
- Python高级特性之:List Comprehensions、Generator、Dictionary and set ...
今天帅气的易哥和大家分享的是Pyton的高级特性,希望大家能和我一起学习这门语言的魅力. Python高级特性之:List Comprehensions.Generator.Dictionary an ...
- 三、python高级特性(切片、迭代、列表生成器、生成器)
1.python高级特性 1.1切片 list列表 L=['Mli','add','sal','saoo','Lkkl'] L[0:3] #即为['Mli','add','sal'] 从索引0开始 ...
- python高级特性:切片/迭代/列表生成式/生成器
廖雪峰老师的教程上学来的,地址:python高级特性 下面以几个具体示例演示用法: 一.切片 1.1 利用切片实现trim def trim(s): while s[:1] == " &qu ...
- python高级特性和高阶函数
python高级特性 1.集合的推导式 列表推导式,使用一句表达式构造一个新列表,可包含过滤.转换等操作. 语法:[exp for item in collection if codition] if ...
- Python高级特性(3): Classes和Metaclasses(转)
原文:Python高级特性(3): Classes和Metaclasses 类和对象 类和函数一样都是Python中的对象.当一个类定义完成之后,Python将创建一个“类对象”并将其赋值给一个同名变 ...
- Python高级特性(2):Closures、Decorators和functools(转)
原文:Python高级特性(2):Closures.Decorators和functools 装饰器(Decorators) 装饰器是这样一种设计模式:如果一个类希望添加其他类的一些功能,而不希望通过 ...
- Python 高级特性介绍 - 迭代的99种姿势 与协程
Python 高级特性介绍 - 迭代的99种姿势 与协程 引言 写这个笔记记录一下一点点收获 测试环境版本: Python 3.7.4 (default, Sep 28 2019, 16:39:19) ...
- Python高级特性(切片,迭代,列表生成式,生成器,迭代器)
掌握了Python的数据类型.语句和函数,基本上就可以编写出很多有用的程序了. 比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: L = [] n = 1 while n ...
- Python 高级特性(1)- 切片
前言 面 tx 被问到 python 的高级特性相关,这里做个补充学习吧 正向范围取值 关键点 首位下标是 0 第一个数字是起始下标,第二个数字是结束下标(但最终结果不包含它) 代码块一 # 正向范围 ...
随机推荐
- webApi实践:开始WebApi 2
1.学习步骤总结 学习网址:http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-you ...
- UVa1212 Duopoly(最小割)
题目大概说有两家通讯公司,它们分别有几个投标,投标各有价值且各个投标都包含几个频道,相同公司的各个投标包含频道都是互不相同的,而频道不能被同时选用.问怎么选择采取哪家公司哪些投标使得价值最大. 如此建 ...
- BZOJ3421 : Poi2013 Walk
最多只有一个连通块大小大于$nk$,所以用hash表进行BFS的时候只扩展$nk$步即可. 时间复杂度$O(n^2k)$. #include<cstdio> typedef long lo ...
- 只有火狐识别的css
写在css里写只有火狐可以识别,其他浏览器不行. 就算写在@media only screen and (min-width: 960px) and (max-width: 1200px){ 里面也行 ...
- (转)STORM启动与部署TOPOLOGY
STORM启动与部署TOPOLOGY 启动ZOOPKEEPER zkServer.sh start 启动NIMBUS storm nimbus & 启动SUPERVISOR storm sup ...
- gulp + webpack 构建多页面前端项目
修改增加了demo地址 gulp-webpack-demo 之前在使用gulp和webpack对项目进行构建的时候遇到了一些问题,最终算是搭建了一套比较完整的解决方案,接下来这篇文章以一个实际项目为例 ...
- TML5如何在移动网页端调用手机图片或者camera
TML5如何在移动网页端调用手机图片或者camera可以参考这篇文章: 如果你开始基于iOS系统(ios6 above) 的web应用,可以考虑这段代码: 点击按钮,会调用你的摄像头相册 附源码文件: ...
- 未能加载文件或程序集"Microsoft.Web.Infrastructure 的解决方案
转载请注明来源: http://www.cnblogs.com/zaiyuzhong/p/Unload-Infrastructure-Solution.html 部署MVC5 项目发布到文件系统 I ...
- 20145304 Java第七周学习报告
20145304<Java程序设计>第七周学习总结 教材学习内容总结 1.时间的度量: 格林威治标准时间(GMT)通过观察太阳而得,其正午是太阳抵达天空最高点之时,因地球的公转与自传,会造 ...
- 如何提升 service 等级,不被kill(整合)
(1)在应用退到后台后,另起一个只有1像素的页面停留在桌面上,让自己保持前台状态,保护自己不被后台清理工具杀死. (2)用Foreground Service. 参考地址:Android Servic ...