004_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 counterTrue>>> 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/
004_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进阶:全面解读高级特性之切片!
导读:切片系列文章连续写了三篇,本文是对它们做的汇总.为什么要把序列文章合并呢?在此说明一下,本文绝不是简单地将它们做了合并,主要是修正了一些严重的错误(如自定义序列切片的部分),还对行文结构与章节衔 ...
- ActiveMQ中的Destination高级特性(一)
---------------------------------------------------------------------------------------- Destination ...
- Python3学习(二)-递归函数、高级特性、切片
##import sys ##sys.setrecursionlimit(1000) ###关键字参数(**关键字参数名) ###与可变参数不同的是,关键字参数可以在调用函数时,传入带有参数名的参数, ...
- 云端卫士实战录 | Java高级特性之多线程
<实战录>导语 一转眼作为一名Java开发者已经四年多时间了,说长不长说短不短,对于java的感情还是比较深的,主要嘛毕竟它给了我饭吃.哈哈,开个玩笑.今天我想借此机会来和大家聊聊Java ...
- javascript高级特性
01_javascript相关内容02_函数_Arguments对象03_函数_变量的作用域04_函数_特殊函数05_闭包_作用域链&闭包06_闭包_循环中的闭包07_对象_定义普通对象08_ ...
- Visual Studio 2015 速递(4)——高级特性之移动开发
系列文章 Visual Studio 2015速递(1)——C#6.0新特性怎么用 Visual Studio 2015速递(2)——提升效率和质量(VS2015核心竞争力) Visual Studi ...
- Android TextView高级特性使用
TextView一般都是用来显示一段文本,这里说的高级特性主要是一些我们平常不太常用的属性.包括文字阴影.自定义字体.html嵌入多格式.字体加粗.插入图片.这些特性平时开发APP的时候,可能一般使用 ...
随机推荐
- css3巧用选择器配合伪元素
一 . 前言 有时我们在写底部导航栏时,会有很多超链接,每个链接间用“|”分割,如下图: 可能刚入门的朋友会想到这样完成,再单独设置span的样式, 今天主要介绍怎么样用css3简单快速的完成这个效果 ...
- adb for mac
1.Install homebrew ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/mas ...
- 【转录】原来Github上的README.md文件这么有意思——Markdown语言详解
之前一直在使用github,也在上面分享了不少的项目和Demo,每次创建新项目的时候,使用的都是默认的README.md文件,也不曾对这个文件有过什么了解.但是在看到别人写的项目的README.md里 ...
- CSS使用position:sticky 实现粘性布局
简介 前面写了一篇文章讲解了position常用的几个属性:<CSS 属性之 position讲解>一般都知道下面几个常用的: { position: static; position: ...
- RabbitMQ 环境搭建
安装基础环境 yum install net-tools yum install yum yum install gcc glibc-devel make ncurses-devel openssl- ...
- springmvc 获取请求头信息
@PostMapping("/test/post") public void post(@RequestBody String params, @RequestHeader(req ...
- concrrent类下 BlockingDeque 下 自己实现代码编写
一.LinkedBlockingDeque简介 java6增加了两种容器类型,Deque和BlockingDeque,它们分别对Queue和BlockingQueue进行了扩展. Deque是一个双端 ...
- C# 利用ReportViewer生成报表
本文主要是利用微软自带的控件ReportViewer进行报表设计的小例子,仅供学习分享使用,如有不足之处,还请指正. 涉及知识点: ReportViewer :位于Microsoft.Reportin ...
- django rest framework 与 Vue 整合遇到的坑
前提是已经有了Django项目与前端Vue打包好的dist文件 好,开始整合!!! 当然还是先设置Django的setting.py 1设置模板路径 2 设置静态文件路径 TEMPLATES = [ ...
- MFC Bresesnham算法
Bresesnham算法绘制直线段 Bresenham算法的意义:高效的将图形光栅化.其计算过程中均采用加法运算,故大大减少了程序的开销. 绘制直线段(MFC中) //传入参数:起点.终点,颜色 vo ...