第四章:迭代器和生成器

迭代器是Python中最强有力的特性之一,从最高层次看,我们可以简单地把迭代看做是一种处理序列中元素的方式。但是这里还有更多的可能,比如创建自己的可迭代对象、在itertools模块中选择使用的迭代模式,构建生成器函数等;

1.手动访问迭代器中的元素

问题:我们需要处理某个可迭代对象中的元素,但是基于某种原因不能也不想使用for循环

解决方案:用手动访问可迭代对象中的元素,可以使用next()函数,然后自己编写代码来捕获stopIteration异常;

>>>with open('/ect/passwd') as f:
>>>    try:
>>>        while True:
>>>            line = next(f)
>>>            print(line,end='')
>>>    except StopIteration:
>>>        pass

一般来说,StopIteration异常时用来通知我们迭代结束的。但是如果是手动使用next(),也可以命令他返回一个结束值

  • 大多数情况下,我们会用for语句来访问可迭代对象中的元素。但是偶然也会碰到需要对底层迭代机制做更精细控制的情况。
>>> items = [1,2,3]
>>> it = iter(items)
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

2.委托迭代

问题:我们构建了一个自定义的对象容器,其内部持有一个列表,元祖或其他的可迭代对象。我们想让自己的新容器能够完成迭代操作;

解决方案:一般来说,我们所要做的就是定义一个iter()方法,将迭代请求委托到对象内部持有的容器上;

3.用生成器创建新的迭代模式

问题:我们想要实现一个自定义的迭代模式,使其区别于常见的内建函数(即range()、reversed()等)

解决方案:如果想要实现一种新的迭代模式,可使用生成器函数来定义,这里有一个生成器可产生某个范围内的浮点数;

>>> def frange(start,stop,increment):
...     x = start
...     while x < stop:
...             yield x
...             x += increment
...
>>> for n in frange(0,4,0.5):
...     print(n)
...
0
0.5
1.0
1.5
2.0
2.5
3.0
3.5
  • 函数中只要出现了yield语句就会将其转变成一个生成器。与普通函数不同,生成器只会在响应迭代操作时才运行。

4.实现迭代协议

问题:我们正在构建一个自定义的对象,希望他可以支持迭代操作,但是也希望有一种简单的方式来实现迭代协议;

解决方案:目前来看,要在对象上实现可迭代功能,最简单的方式就是使用生成器函数;

5.反向迭代

>>> a = [1,2,3,4,5]
>>> for x in reversed(a):
...     print(x)
...
5
4
3
2
1
  • 方向迭代只有在待处理的对象拥有可确定的大小,或者对象实现了reversed()特殊方法时,才能奏效。如果这两个条件都无法满足,则必须首先将这个对象转换为列表;
>>>class Countdown:
>>>
>>> def __init__(self,start):
>>>     self.start = start
>>>     
>>> #Forward iterator
>>> def __iter__(self):
>>>     n = self.start
>>>     while n > 0:
>>>         yield n
>>>         n -= 1
>>> 
>>> #Reversed iterator
>>> def __reversed__(self):
>>>     n = 1
>>>     while n < self.start:
>>>         yield n 
>>>         n += 1

6.定义带有额外状态的生成器函数

7.对迭代器作切片操作

8.跳过迭代对象中的前一部分元素

9.迭代所有可能的组合或排列

10.以索引-值对的形式迭代序列

问题:我们想迭代一个序列,但是又想记录下序列中当前处理到元素的索引

解决方案:内建的enumerate()函数可以非常漂亮的解决这个问题:

>>> my_list = ['a','b','c']
>>> for idx,val in enumerate(my_list,1):
...     print(idx,val)
...
1 a
2 b
3 c
>>> for idx,val in enumerate(my_list):
...     print(idx,val)
...
0 a
1 b
2 c

enumerate()可以方便地用来跟踪特定值出现在列表中的偏移位置,比如,如果想将文件中的单词和他们所出现的行之间建立映射关系,可以通过使用enumerate()来将每个单词映射到文件行相应的偏移位置来实现:

>>> from collections import defaultdict
>>> word_summary = defaultdict(list)
>>> with open('D:\\project\\test.txt','r') as f:
...     lines = f.readlines()
...
>>> for idx,line in enumerate(lines):
...     words = [w.strip().lower() for w in line.split() ]
...     for word in words:
...             word_summary[word].append(idx)
...
>>> print(word_summary)
defaultdict(<class 'list'>, {'elasticsearch': [0, 2, 4, 6, 7, 7, 8, 8], 'is': [0, 0, 5, 5], 'a': [0, 2, 6, 6, 6, 7, 8, 8], 'highly': [0], 'scalable': [0], 'open-source': [0], 'full-text': [0], 'sea

处理完文件之后,如果打印word_summary,将得到一个字典(准确的说是一个defaultdict),而且每个单词都是字典的键,每个单词键所对应的值就是由行号组成的列表,表示这个单词层出现过的所有行。如果但是在一行中出现过2次,那么这个行号就会记录2次;

11.同时迭代多个序列

我们想要迭代的元素包含在多个序列中,我们想同时对他们进行迭代;

解决方案:可以使用zip()函数来同时迭代多个序列;

>>> xpts = [1,5,4,2,10,7]
>>> ypts = ['a','b','c','d','e','f']
>>> for x,y in zip(xpts,ypts):
...     print(x,y)
...
1 a
5 b
4 c
2 d
10 e
7 f

zip(a,b)的工作原理是创建一个迭代器,该迭代器可产生出元祖(x,y),这里的x取自a,y取自序列b,当其中某个输入序列没有元素可以继续迭代时,整个迭代过程结束;

>>> xpts = [1,5,4,2,10,7]
>>> ypts = ['a','b','c']
>>> for x,y in zip(xpts,ypts):
...     print(x,y)
...
1 a
5 b
4 c

如果这种行为不是所需要的,可以使用itertools.zip_longest()来替代

>>> from itertools import zip_longest
>>> for i in zip_longest(xpts,ypts):
...     print(i)
...
(1, 'a')
(5, 'b')
(4, 'c')
(2, None)
(10, None)
(7, None)

zip()通常用在需要将不同的数据配对在一起时。

>>> headers = ['name','shares','price']
>>> values = ['overad','100','490.1']
>>> s = dict(zip(headers,values))
>>> for name,val in zip(headers,values):
...     print(name,'=',val)
...
name = overad
shares = 100
price = 490.1
>>> print(s.keys())
dict_keys(['name', 'shares', 'price'])
>>> print(s.values())
dict_values(['overad', '100', '490.1'])

最后需要重点强调的是,zip()函数创建出的结果只是一个迭代器,如果需要将配对的数据保存为列表,那么请使用list()函数;

>>> zip(xpts,ypts)
<zip object at 0x0000000003C03648>
>>> list(zip(xpts,ypts))
[(1, 'a'), (5, 'b'), (4, 'c')]

12.在不同的容器中进行迭代

问题:我们需要对许多对象执行相同的操作,但是这些对象包含在不同的容器内,而我们希望可以避免写出嵌套的循环处理,保持代码的可读性;

解决方案:itertools.chain()方法可以用来简化这个任务。他可以接受一系列可迭代对象并返回一个迭代器。这个迭代器能够有效的掩盖一个事实:你实际上实在对多个容器进行迭代;

>>> from itertools import chain
>>> a = [1,2,3,4,5]
>>> b = ['x','y','z']
>>> for x in chain(a,b):
...     print(x)
...
1
2
3
4
5
x
y
z

在程序中,chain()常见的用途是想一次性对所有的元素执行某项特定的操作,但是这些元素分散在不同的集合中;

>>>active_items = set()
>>>inactive_items = set()
>>>#Iterate over all items
>>>for item in chain(active_items,inactive_items):
...      #Process item

13.创建处理函数的管道

14.扁平化处理嵌套型的序列

15.合并多个有序序列,再对整个有序序列进行迭代

问题:我们有一组有序序列,想对他们合并在一起后的有序序列进行迭代;

解决方案:heapq.merge()函数正是我们所需要的。

>>> import heapq
>>> a = [1,3,6,8,9,12]
>>> b = [2,4,5,10]
>>> for c in heapq.merge(a,b):
...     print(c)
...
1
2
3
4
5
6
8
9
10
12

heapq.merge()函数要求所有的序列都是有序的;

16.用迭代器取代while循序

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,896评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,283评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,085评论 0 367
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,010评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,015评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,492评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,858评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,829评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,374评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,409评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,527评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,131评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,858评论 3 339
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,296评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,434评论 1 276
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,087评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,597评论 2 366

推荐阅读更多精彩内容