迭代器
迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。 迭代器不能回退,只能往前进行迭代。这并不是什么很大的缺点,因为人们几乎不需要在迭代途中进行回退操作。 迭代器也不是线程安全的,在多线程环境中对可变集合使用迭代器是一个危险的操作。但如果小心谨慎,或者干脆贯彻函数式思想坚持使用不可变的集合,那这也不是什么大问题。 对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值,这是后话)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。 迭代器的另一个优点就是它不要求你事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。这个特点被称为延迟计算或惰性求值(Lazy evaluation)。 迭代器更大的功劳是提供了一个统一的访问集合的接口。只要是实现了__iter__()方法的对象,就可以使用迭代器进行访问。
创建迭代
对对象调用一个iter(),就可以得到它的迭代器,
###语法: iter(obj) #一个参数 iter(func, sentinel ) #两个参数
如果你传递一个参数给 iter() , 它会检查你传递的是不是一个序列, 如果是, 那么很简单:根据索引从 0 一直迭代到序列结束. 另一个创建迭代器的方法是使用类, 我们将在第 13 章详细介绍, 一个实现了 __iter__() 和 next() 方法的类可以作为迭代器使用.
如果是传递两个参数给 iter() , 它会重复地调用 func , 直到迭代器的下个值等于sentinel .
关于迭代
根本上说, 迭代器就是有一个 next() 方法的对象, 而不是通过索引来计数. 当你或是一个循环机制(例如 for 语句)需要下一个项时, 调用迭代器的 next() 方法就可以获得它. 条目全部取出后, 会引发一个 StopIteration 异常, 这并不表示错误发生, 只是告诉外部调用者, 迭代完成.
例如 for 循环遍历,这种形式的访问清晰、简洁、方便。这种迭代器的用法在 Python 中普遍而且统一。在后台,for 语句在容器对象中调用 iter() 。 该函数返回一个定义了 next() 方法的迭代器对象,它在容器中逐一访问元素。没有后续的元素时,next() 抛出一个 StopIteration 异常通知 for 语句循环结束。
例如: For循环: >>> for i in 'abc': print i a b c 实际运行原理: >>> s = 'abc' >>> it = iter(s) >>> it <iterator object at 0x00A1DB50> >>> it.next() 'a' >>> it.next() 'b' >>> it.next() 'c' >>> it.next() Traceback (most recent call last): File "<pyshell#6>", line 1, in -toplevel- it.next() StopIteration
序列使用迭代器
>>> myTuple = (123, 'xyz', 45.67) >>> i = iter(myTuple) >>> i.next() 123 >>> i.next() 'xyz' >>> i.next() 45.67 >>> i.next() Traceback (most recent call last): File "", line 1, in ? StopIteration
字典使用迭代器
字典和文件是另外两个可迭代的 Python 数据类型. 字典的迭代器会遍历它的键(keys).语句 for eachKey in myDict.keys() 可以缩写为 for eachKey in myDict , 例如:
>>> legends = { ('Poe', 'author'): (1809, 1849, 1976), ... ('Gaudi', 'architect'): (1852, 1906, 1987), ... ('Freud', 'psychoanalyst'): (1856, 1939, 1990) ... } ... >>> for eachLegend in legends: ... print 'Name: %s Occupation: %s' % eachLegend ... print ' Birth: %s Death: %s Album: %s ' ... % legends[eachLegend] ... Name: Freud Occupation: psychoanalyst Birth: 1856 Death: 1939 Album: 1990 Name: Poe Occupation: author Birth: 1809 Death: 1849 Album: 1976 Name: Gaudi Occupation: architect Birth: 1852 Death: 1906 Album: 1987
另外, Python 还引进了三个新的内建字典方法来定义迭代: myDict.iterkeys() (通过 keys 迭代), myDict.itervalues() (通过 values 迭代), 以及 myDicit.iteritems() (通过 key/value 对来迭代).
列表解析
###语法: [expr for iter_var in iterable]
这个语句的核心是 for 循环, 它迭代 iterable 对象的所有条目. 前边的 expr 应用于序列的每个成员, 最后的结果值是该表达式产生的列表. 迭代变量并不需要是表达式的一部分.
#例如: >>> map(lambda x: x ** 2, range(6)) [0, 1, 4, 9, 16, 25] #可以由下面的列表解析替换: >>> [x ** 2 for x in range(6)] #可以写成[(x ** 2) for x in range(6)],更清楚 [0, 1, 4, 9, 16, 25]
列表解析的表达式可以取代内建的 map() 函数以及 lambda , 而且效率更高. 结合if语句,列表解析还提供了一个扩展版本的语法:
[expr for iter_var in iterable if cond_expr]
这个语法在迭代时会过滤/捕获满足条件表达式 cond_expr 的序列成员.
###样例:序列中的奇数 >>> seq = [11, 10, 9, 9, 10, 10, 9, 8, 23, 9, 7, 18, 12, 11, 12] >>> [x for x in seq if x % 2] [11, 9, 9, 9, 23, 9, 7, 11]
#如下这样一个数据文件 hhga.txt , 需要计算出所有非空白字符的数目: >>> f = open('hhga.txt', 'r') >>> len([word for line in f for word in line.split()]) #嵌套for循环 91