|
分歧
所有类序列或类迭代器的主要相似之处是,它们都允许进行循环遍历,无论是使用 for 循环、列表理解(list comprehension)还是生成器理解(generator comprehension)。除此之外,就出现了分歧。其中最重要的差异是,序列既可编制索引,也可直接切片(slice),而迭代器不能。实际上,为序列编制索引可能是最常用的序列操作 —— 究竟为什么在迭代器上无法使用索引呢?例如:
清单 9. 与序列和迭代器相似的东西 >>> r = range(10) >>> i = ITer(r) >>> x = xrange(10) >>> g = itertools.takewhile(lambda n: n<10, ITertools.count()) #...etc...
对于所有这一切,都可以使用 for n in thing。实际上,如果用 list(thing) 显示它们,会得到完全相同的结果。但是,如果希望获得其中的一个特定条目(或一些条目的切片),就需要考虑 thing 的具体类型。例如:
清单 10. 索引操作成功和失败的示例 >>> r[4] 4 >>> i[4] TypeError: unindexable object
对于每种序列/迭代器类型,只要费一番功夫,总能够获得其中的特定条目。一种方法是进行循环,直至到达所需的对象。另一种古怪的解决方案如下:
清单 11. 获得索引的怪方法 >>> thing, temp = ITertools.tee(thing) >>> zip(temp, '.'*5)[-1][0] 4
对 itertools.tee() 的预调用保留了原始迭代器。对于切片,可以按照特殊方式使用 ITertools.islice() 函数。
清单 12. 获得一个切片的特殊方法 >>> r[4:9:2] [4, 6, 8] >>> list(itertools.islice(r,4,9,2)) # works for ITerators [4, 6, 8]
类包装器
为了方便起见,可以将这些技术组合成一个类包装器:
清单 13. 使迭代器可编制索引
所以,通过某些措施,可以让一个对象同时表现得像序列和迭代器。但是,费这么大力气实际上 应该是不必要的;无论涉及的是序列还是迭代器,编制索引和切片都应该是 “可行的”。
注意,Indexable 类包装器仍然不够灵活。主要问题是,每次都要创建迭代器的一个新副本。更好的方法应该是在对序列切片时缓存序列的头部,然后在以后访问已经检查的元素时使用所缓存的头部。当然,在使用迭代器时,要在占用的内存和速度之间有所取舍。但是,如果 Python 本身能够 “在幕后” 完成这些,就再好不过了 —— “高级用户” 可以对行为进行调优,而一般程序员应该不需要考虑这些。
在本系列的下一期中,我将讨论如何使用属性语法访问方法。
上一页 [1] [2] [3] |