简介
在前面的一篇文章里,我简单的介绍了一下python iterator的一些基本定义和使用思路。从表面上来看,iterator只是迭代的去访问一组内容,在实际使用的过程中如果结合一些其他的手法,能够用一种很简练的方式实现一些很强大的功能。这里一并做一个总结。
使用总结
Iterator的基本使用方法
我们知道,常用的iterator使用方法有直接的for循环。而从前面的文章里也看到,在某些情况下,因为iterator里面有定义了__next__()方法,我们可以调用next()方法来访问它。
比如说我们有一个文件test.txt,我们需要读出来里面的内容。一个最常用的方法如下:
with open('test.txt') as f: ... for line in f: ... print(line, end='')
我们也可以采用对应的next()方法,对应实现的代码如下:
with open('test.txt') as f: ... try: ... while True: ... line = next(f) ... print(line, end='') ... except StopIteration: ... pass
我们知道,整因为iterator内部的实现是通过StopIteration这个异常来保证运行的结束,所以这里才需要捕捉这个异常来作为访问结束的退出。和前面的方法比起来,这个显得稍微臃肿了一点。当然,我们可以对这些代码稍微改进一点,毕竟只是一个判断迭代器是否走完,用异常显得太笨重。改进后的代码如下:
with open('test.txt') as f: while True: line = next(f, None) if line is None: break print(line, end='')
这里的一个区别就是next()方法多带了一个参数None, 这里表示如果后面读取不到内容了,则返回None,这样我们就不用再去通过捕捉异常的方式来判断,只要判断一下读取到的内容是否为None就可以了。
这几种方式的比较,第一种显然要简单一些。只是在某些情况下如果我们没法用for循环去访问迭代器的时候,可以考虑用后面这种方式。
反向移动Iterator
在通常情况下,我们迭代器都是从头到尾的去遍历元素集合,但是在某些特殊的情况下,比如说我们需要从集合的末位遍历到开头,那该怎么来使用iterator呢?一个简单的办法就是使用reversed()方法。最常用的方式在于对一个list操作:
>>> a = [1, 2, 3, 4] >>> for x in reversed(a): ... print(x) ... 4 3 2 1
在前面访问文件的示例中,如果我们也希望达到这样的效果,则需要将文件内容先读进内存,然后按照相反的顺序输出,这种实现的方式如下:
f = open('test.txt') for line in reversed(list(f)): print(line, end='')
这种方式有一个潜在的问题,就是对于比较小的文件是可行的,对于比较大的文件,如果不可能将所有内容都装载到内存中的话,这种方法就不可行了。需要通过f.seek()先找到文件的末位,然后再倒过来一步步的往前读。
我们知道,对于一个对象来说,如果它实现了 __iter__方法,相当于实现了一个迭代器的返回定义方法。所以我们可以通过iter()来取得这个对象的迭代器。然后我们再通过循环来遍历它。对于反向遍历,我们也可以定义类似的方法实现:
class Countdown: def __init__(self, start): self.start = start def __iter__(self): n = self.start while n > 0: yield n n -= 1 def __reversed__(self): n = 1 while n <= self.start: yield n n += 1
这里相当于定义了两个迭代器,一个是正向的,一个是反向的。我们可以这样来使用它们:
>>> for n in iter(c): ... print(n) ... 10 9 8 7 6 5 4 3 2 1 >>> for n in reversed(c): ... print(n) ... 1 2 3 4 5 6 7 8 9 10
Iterator数据切片
有的时候我们希望取迭代器数据中间的一部分。一种典型的思路就是我用一个计数器,专门计算取到那个范围的再统一返回。实际上这里有一种比较简单的手法。就是使用itertools.islice()方法。以前面我们读取文件的代码为例:
>>> with open('test.txt') as f: ... import itertools ... for x in itertools.islice(f, 2, 4): ... print(x) ... ghi jkl
这里实际返回的是第3行和第4行的内容。实际上itertools.islice()方法通过指定的参数(2, 4)确定了取的值范围为第3,4行。然后通过丢弃前面的行,直到进入到我们所要求的数据范围。这样就省略了很多我们自己来写一个计数器跟踪它们的代码。islice()方法可以起到一个很好的数据范围过滤效果。如果我们设定itertools.islice(f, 2, None),则相当于取数组f[2:]的效果。
Iterator内容过滤
我们在遍历一些集合的时候,希望过滤掉一部分元素。这个时候,一个典型的方法就是我们所想到的list comprehension,比如说,我们要输出一个文件的内容,但是要跳过里面所有加了#号的注释部分。我们可以这样来做:
>>> with open('test.txt') as f: ... lines = (line for line in f if not line.startswith('#')) ... for line in lines: ... print(line, end='')
如果有的时候我们只是为了跳过文件开头的那部分注释呢?还有一个更加简单的办法,就是利用itertools里的dropwhile方法。这种用法如下:
>>> from itertools import dropwhile >>> with open('test.txt') as f: ... for line in dropwhile(lambda line: line.startswith('#'), f): ... print(line, end='')
同时迭代多个序列
有时候我们希望能够同时遍历多个序列,比如有序列a = [1, 2, 3, 4, 5], b = ['a', 'b', 'c', 'd', 'e'],我们如果要同时遍历的话,可以采用如下的方式:
>>> xpts = [1, 5, 4, 2, 10, 7] >>> ypts = [101, 78, 37, 15, 62, 99] >>> for x, y in zip(xpts, ypts): ... print(x, y) ... 1 101 5 78 4 37 2 15 10 62 7 99因为使用了zip()方法,我们将两个集合里的内容都同时取出来,按照tuple的方式一个个的组织起来。所以我们访问的时候也是通过一个个tuple的方式来读取。这里我们提供的两个list是长度一致的,如果不一致会怎么样呢?我们再来试试另外两个序列:
>>> a = [1, 2, 3, 4] >>> b = ['a', 'b', 'c'] >>> for x, y in zip(a, b): ... print(x, y) ... 1 a 2 b 3 c从代码运行的结果来看,默认是遍历到短的那个序列结束。如果我们需要到那个长的序列结束呢?这里有另外一种办法:
>>> from itertools import zip_longest >>> for i in zip_longest(a, b): ... print(i) ... (1, 'a') (2, 'b') (3, 'c') (4, None)这里引用了zip_longest方法,它可以将两个序列组合起来,不过对于短的那个序列,用None来补齐。
将几个序列串在一起
我们可以直接看如下的代码:
>>> from itertools import chain >>> a = [1, 2, 3, 4] >>> b = ['a', 'b', 'c'] >>> for x in chain(a, b): ... print(x) ... 1 2 3 4 a b c一个chain方法就解决了大部分问题了。和我们默认想到的方法比起来,chain方法效率更加高。因为我们最开始会考虑将两个或者多个序列连在一起,比如a + b,这样会创造一个新的序列出来,这样带来的成本开销明显偏大了。
将嵌套的序列变平
这是一个有意思的问题,因为一般来说当我们需要访问一个数组的时候,比如说a = [1, 2, [3, 4, [5, 6], 7, 8], 9, 10],我们希望能够将他们所有的元素都输出,并使得他们看起来像就是一个一维数组那样,如a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]。我们默认的思路该怎么办呢?只怕一开始就是碰到一个元素的时候会判断它是否为数组,如果是的则递归的去输出它的元素。
Python里面有一个很强大的特性可以很好的实现这个方法:
from collections import Iterable def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): yield from flatten(x) else: yield x这种实现里面有一个额外的ignore_types,里面列举了一些类型我们可以不需要进一步的去遍历。比如说str,我们一般碰到一个字符串可以直接将他们作为一个整的对象输出而不是再对它们进一步拆分的遍历。最有意思的地方在yield from这个部分。yield from这个部分的意思是将后续的值作为它本身的一个subroutine。所以它们就会被当作一个拉平的数组。关于yield from这部分我们在后面的文章中会专门讲述。
按照这个方式,我们使用它们的代码如下:
>>> from nested import flatten >>> items = [1, 2, [3, 4, [5, 6], 7], 8] >>> for x in flatten(items): ... print(x) ... 1 2 3 4 5 6 7 8
迭代多个有序排列数组
这个问题不太好用一句话描述,就是说假定我们有若干个已经排序的数组了。当我们希望能够去遍历这所有的序列,但是保证我们每次都取出他们中间最小的元素,保证所有输出还是一个严格排序的结果,我们该怎么办呢?实际上,这是一个多路归并排序的问题。在前面的一些文章里有过讨论,不过要做一个好的java实现我们可是费了一番功夫。这里有什么好的招呢?
>>> import heapq >>> a = [1, 4, 7, 10] >>> b = [2, 5, 6, 11] >>> for c in heapq.merge(a, b): ... print(c) ... 1 2 4 5 6 7 10 11
这里是归并两路的数据结果。在一些我们如果要归并多个文件的情况下,也可以这样来做。因为这里heapq.merge不是一次将所有的数据都装载到内存里,它只是每次取很小的一部分,像generator一样。所以对于大文件的合并用这种方式来做。呵呵,寥寥几行代码就解决了问题,不能不说,很好很强大啊。
总结
Iterator的定义方法虽然看起来很简单,但是它的使用也可以非常的复杂和灵活。通过结合一些库的支持,我们可以实现非常强大的计算效果。当然,前提是我们需要知道去哪里找到这些库和知道这些用法。
参考材料
python cookbook
相关推荐
- **内置类型的变化**:例如`long`类型在Python 3中被`int`类型所取代,`iterators`中的`next()`方法被`next(iterator)`所替代。 - **异常处理**:Python 3对异常类进行了重新组织,例如`StandardError`被`Exception...
由于文档内容部分重复,并且主要内容是关于加入一个非盈利性的Python学习交流群和获取免费Python书籍的信息,这部分内容并没有提供更多的Python知识和练习题的具体信息,因此我们不再重复描述这部分内容。...
生成器(Generator)是Python中的一种迭代器(Iterator),它可以创建一个可以迭代的对象,但不会一次性将所有元素加载到内存中,而是按需生成。例如,g=((i+2)**2 for i in range(10))创建了一个生成器对象g,该...
这部分涵盖了Python 2.7中一些较为高级或不常用的功能,如`apply()`用于将一个函数应用于多个参数,`buffer()`用于创建缓冲区对象等。 #### 四、内置常量 这一章节列出了Python 2.7中的内置常量,包括: - `True`...
《精通Python设计模式》第二版是一本深入探讨Python编程中设计模式的专业书籍,它为Python开发者提供了丰富的知识和实践指导。设计模式是软件工程中经过验证的、在特定上下文中解决常见问题的模板,是经验丰富的...
除了理论知识外,《Beginning Python》还可能包含一些实际项目案例,以便读者能够在实践中加深对Python的理解和应用能力。这些项目可能涉及Web爬虫、数据分析、图形界面设计等方面。 #### 总结 《Beginning Python...
4. **定义帧延迟**:每帧之间需要有一定的间隔时间,这可以通过设置`ImageSequence.Iterator`的`duration`参数来实现,单位为毫秒。 5. **保存为GIF**:最后,使用`save()`方法将帧序列保存为GIF文件。注意要设置`...
本文将围绕如何利用Python对上亿规模的数据进行分块处理展开讨论,包括具体的实现方法、涉及的关键技术和注意事项。 #### 核心知识点解析 **1. 使用Pandas进行CSV文件的大规模读取** 在给定的示例中,首先通过...
本文实例讲述了python 协程中的迭代器,生成器原理及应用。分享给大家供大家参考,具体如下: 1.迭代器理解 迭代器: 迭代器是访问可迭代对象的工具 迭代器是指用iter(obj)函数返回的对象(实例) 迭代器是指用next(it...
- **迭代器与生成器**:深入探讨迭代器(iterator)和生成器(generator)的工作机制及其应用场景。 - **元编程**:了解如何利用元编程技术来动态地创建类或修改类的行为。 以上这些知识点构成了学习Python语言的基础...
虽然部分内容并未给出具体章节或段落,但从标题和描述来看,这份文档应该是一本全面介绍Python语言特性和使用的参考书。下面将从几个方面来阐述Python的核心知识点。 ### Python简介 Python是一种解释型、面向对象...
在Java、Python、C#等面向对象语言中,迭代器模式都有其对应的应用。例如,在Java中,`Iterable`接口和`Iterator`接口就是实现迭代器模式的关键。`Iterable`接口定义了获取迭代器的方法`iterator()`,而`Iterator`...
同时,文章还提到,在实际应用中,读者需要根据实际情况调整切割数据块的大小,以及优化代码来适应不同的需求。此外,还需要考虑异常处理机制,确保在读取和写入数据的过程中程序可以稳定运行。
无论是通过生成器、迭代器等技术来减少内存消耗,还是通过选择合适的数据结构、使用局部变量、缓存以及并行处理等方法来提升程序性能,都能够显著改善Python应用程序的表现。掌握这些技巧不仅可以帮助开发者解决日常...
本专题涵盖了多种设计模式的示例代码,包括Python和C++两种编程语言实现,旨在帮助开发者更好地理解和应用这些模式。 1. **Builder模式**:Builder模式是一种创建型设计模式,它提供了一种创建对象的抽象方法,使得...
本实验主要目的是帮助学生深入理解Python中几种重要的组合数据类型,包括列表(list)、元组(tuple)、字典(dictionary)和集合(set),同时掌握迭代器(iterator)的概念及其应用。通过实际编程练习,学生能够...
这种遍历方式广泛应用于各种数据结构,如列表(list)、字符串(string)、字典(dict)等。 #### 二、迭代器协议 为了使一个对象成为迭代器,它必须满足以下两个条件: 1. **`__iter__()` 方法**:这个方法应该...
尽管文档中的内容重复提及了加入一个非营利性质的Python学习交流群组的信息,并提供了丰富的书籍资源,但并没有直接给出具体的编程知识点或练习题目的细节。因此,下面将基于文档的标题、描述及部分提供的上下文来...
Python设计模式是软件开发中的一种最佳实践,它总结了在特定上下文中解决常见问题的经验和方法。..."设计模式_27"这个资源包很可能是为每个模式提供了一个具体的Python示例,帮助读者理解并实践这些模式。
分享给大家供大家参考,具体如下: 迭代器模式(Iterator Pattern):提供方法顺序访问一个聚合对象中各元素,而又不暴露该对象的内部表示. 下面是一个迭代器模式的demo: #!/usr/bin/env python # -*- coding:utf-8 -...