流畅的python 14章可迭代的对象、迭代器 和生成器
可迭代的对象、迭代器和生成器
迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。
迭代器用于从集合中取出元素;而生成器用于“凭空”生成元素。通过斐波纳契数列能很好地说明二者之间的区别:斐波纳契数
列中的数有无穷个,在一个集合里放不下。不过要知道,在 Python社区中,大多数时候都把迭代器和生成器视作同一概念。
Sentence类
单词序列
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text) ➊
def __getitem__(self, index):
return self.words[index] ➋
def __len__(self): ➌
return len(self.words)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text) ➍
❶re.findall 函数返回一个字符串列表,里面的元素是正则表达式的
全部非重叠匹配。
❷ self.words 中保存的是 .findall 函数返回的结果,因此直接返回
指定索引位上的单词。
❸ 为了完善序列协议,我们实现了 __len__ 方法;不过,为了让对象
可以迭代,没必要实现这个方法。
❹ reprlib.repr 这个实用函数用于生成大型数据结构的简略字符串表
示形式。
示例 14-2 测试 Sentence 实例能否迭代
>>> s = Sentence('"The time has come," the Walrus said,') # ➊
>>> s
Sentence('"The time ha... Walrus said,') # ➋
>>> for word in s: # ➌
... print(word)
The
time
has
come
the
Walrus
said
>>> list(s) # ➍
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said']
❶ 传入一个字符串,创建一个 Sentence 实例。
❷ 注意,__repr__ 方法的输出中包含 reprlib.repr 方法生成的
...。
❸ Sentence 实例可以迭代,稍后说明原因。
❹ 因为可以迭代,所以 Sentence 对象可以用于构建列表和其他可迭代
的类型。
序列可以迭代的原因:iter函数
解释器需要迭代对象 x 时,会自动调用 iter(x)。内置的 iter 函数有以下作用。
- (1) 检查对象是否实现了 __iter__ 方法,如果实现了就调用它,获取一个迭代器。
- (2) 如果没有实现 __iter__ 方法,但是实现了 __getitem__ 方法,Python 会创建一个迭代器,尝试按顺序(从索引 0 开始)获取元素。
- (3) 如果尝试失败,Python 抛出 TypeError 异常,通常会提示“C objectis not iterable”(C 对象不可迭代),其中 C 是目标对象所属的类。任何 Python 序列都可迭代的原因是,它们都实现了 __getitem__ 方法。其实,标准的序列也都实现了 __iter__ 方法,因此你也应该这么做。之所以对 __getitem__ 方法做特殊处理,是为了向后兼容,而未来可能不会再这么做
可迭代的对象与迭代器的对比
可迭代的对象
使用 iter 内置函数可以获取迭代器的对象。如果对象实现了能返回迭代器的 __iter__ 方法,那么对象就是可迭代的。序列都可以迭代;实现了 __getitem__ 方法,而且其参数是从零开始的索引,这种
对象也可以迭代。
我们要明确可迭代的对象和迭代器之间的关系:Python 从可迭代的对象中获取迭代器。
下面是一个简单的 for 循环,迭代一个字符串。这里,字符串 'ABC'是可迭代的对象。背后是有迭代器的,只不过我们看不到:
>>> s = 'ABC'
>>> for char in s:
... print(char)
...
A
B
C
###如果没有 for 语句,不得不使用 while 循环模拟,要像下面这样写:
>>> s = 'ABC'
>>> it = iter(s) # ➊
>>> while True:
... try:
... print(next(it)) # ➋
... except StopIteration: # ➌
... del it # ➍
... break # ➎
...
A
B
C
❶ 使用可迭代的对象构建迭代器 it。
❷ 不断在迭代器上调用 next 函数,获取下一个字符。
❸ 如果没有字符了,迭代器会抛出 StopIteration 异常。
❹ 释放对 it 的引用,即废弃迭代器对象。
❺ 退出循环。
StopIteration 异常表明迭代器到头了。Python 语言内部会处理 for循环和其他迭代上下文(如列表推导、元组拆包,等等)中的StopIteration 异常。
标准的迭代器接口有两个方法。
__next__
返回下一个可用的元素,如果没有元素了,抛出 StopIteration异常。
__iter__
返回 self,以便在应该使用可迭代对象的地方使用迭代器,例如在 for 循环中。
迭代器
迭代器是这样的对象:实现了无参数的 __next__ 方法,返回序列中的下一个元素;如果没有元素了,那么抛出 StopIteration 异常。Python 中的迭代器还实现了 __iter__ 方法,因此迭代器也可以迭代。
典型的迭代器
使用迭代器模式实现 Sentence 类
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self): #与前一版相比,这里只多了一个 __iter__ 方法。这一版没有
#__getitem__ 方法,为的是明确表明这个类可以迭代,因为实现了
#__iter__ 方法。
return SentenceIterator(self.words) #根据可迭代协议,__iter__ 方法实例化并返回一个迭代器。
class SentenceIterator:
def __init__(self, words):
self.words = words #SentenceIterator 实例引用单词列表。
self.index = 0#self.index 用于确定下一个要获取的单词。
def __next__(self):
try:
word = self.words[self.index] #获取 self.index 索引位上的单词。
except IndexError:
raise StopIteration() #如果 self.index 索引位上没有单词,那么抛出 StopIteration 异
常。
self.index += 1
return word
def __iter__(self): #实现 self.__iter__ 方法。
return self
生成器函数
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
for word in self.words:
yield word
return
生成器函数的工作原理
只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。
惰性实现
目前实现的几版 Sentence 类都不具有惰性,因为 __init__ 方法急迫地构建好了文本中的单词列表,然后将其绑定到 self.words 属性上。
这样就得处理整个文本,列表使用的内存量可能与文本本身一样多(或许更多,这取决于文本中有多少非单词字符)。如果只需迭代前几个单词,大多数工作都是白费力气。
只要使用的是 Python 3,思索着做某件事有没有懒惰的方式,答案通常都是肯定的。
re.finditer 函数是 re.findall 函数的惰性版本,返回的不是列表,而是一个生成器,按需生成 re.MatchObject 实例。如果有很多匹配,re.finditer 函数能节省大量内存。我们要使用这个函数让第 4版 Sentence 类变得懒惰,即只在需要时才生成下一个单词。代码如示
例 14-7 所示。示例 14-7 sentence_gen2.py: 在生成器函数中调用 re.finditer生成器函数,实现 Sentence 类
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
for match in RE_WORD.finditer(self.text):
yield match.group()
❶ 不再需要 words 列表。
❷ finditer 函数构建一个迭代器,包含 self.text 中匹配 RE_WORD
的单词,产出 MatchObject 实例。
❸ match.group() 方法从 MatchObject 实例中提取匹配正则表达式的
具体文本。
生成器函数已经极大地简化了代码,但是使用生成器表达式甚至能把代
码变得更简短。
生成器表达式
生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。也就是说,如果列表推导是制造列表的工厂,那么生成器表达式就是制造生成器的工厂。
>>> def gen_AB(): # ➊
... print('start')
... yield 'A'
... print('continue')
... yield 'B'
... print('end.')
...
>>> res1 = [x*3 for x in gen_AB()] # ➋
start
continue
end.
>>> for i in res1: # ➌
... print('-->', i)
...
--> AAA
--> BBB
>>> res2 = (x*3 for x in gen_AB()) # ➍
>>> res2 # ➎
<generator object <genexpr> at 0x10063c240>
>>> for i in res2: # ➏
... print('-->', i)
...
start
--> AAA
continue
--> BBB
end.
❶ gen_AB 函数与示例 14-6 中的一样。
❷ 列表推导迫切地迭代 gen_AB() 函数生成的生成器对象产出的元
素:'A' 和 'B'。注意,下面的输出是 start、continue 和 end.。
❸ 这个 for 循环迭代列表推导生成的 res1 列表。
❹ 把生成器表达式返回的值赋值给 res2。只需调用 gen_AB() 函数,
虽然调用时会返回一个生成器,但是这里并不使用。
❺ res2 是一个生成器对象。
❻ 只有 for 循环迭代 res2 时,gen_AB 函数的定义体才会真正执
行。for 循环每次迭代时会隐式调用 next(res2),前进到 gen_AB 函
数中的下一个 yield 语句。注意,gen_AB 函数的输出与 for 循环中
print 函数的输出夹杂在一起。
使用生成器表达式实现 Sentence类
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))
这里不是生成器函数了(没有 yield),而是使用生成器表达式构建生成器,然后将其返回。不过,最终的效果一样:调用 __iter__ 方法会得到一个生成器对象。
生成器表达式是语法糖:完全可以替换成生成器函数,不过有时使用生成器表达式更便利。
标准库中的生成器函数
用于过滤的生成器函数
模块 | 函数 | 说明 |
itertools |
compress(it, |
并行处理两个可迭代的对象;如果 selector_it |
itertools |
dropwhile(predicate, |
处理 it ,跳过 predicate 的计算结果为真值的元 |
内置 | filter(predicate, it) |
把 it 中的各个元素传给 predicate ,如果 |
itertools |
filterfalse(predicate, |
与 filter 函数的作用类似,不过 predicate 的 |
itertools |
islice(it, stop) 或 |
产出 it 的切片,作用类似于 s[:stop] 或 |
itertools |
takewhile(predicate, |
predicate 返回真值时产出对应的元素,然后立 |
>>> def vowel(c):
... return c.lower() in 'aeiou'
...
>>> list(filter(vowel, 'Aardvark'))
['A', 'a', 'a']
>>> import itertools
>>> list(itertools.filterfalse(vowel, 'Aardvark'))
['r', 'd', 'v', 'r', 'k']
>>> list(itertools.dropwhile(vowel, 'Aardvark'))
['r', 'd', 'v', 'a', 'r', 'k']
>>> list(itertools.takewhile(vowel, 'Aardvark'))
['A', 'a']
>>> list(itertools.compress('Aardvark', (1,0,1,1,0,1)))
['A', 'r', 'd', 'a']
>>> list(itertools.islice('Aardvark', 4))
['A', 'a', 'r', 'd']
>>> list(itertools.islice('Aardvark', 4, 7))
['v', 'a', 'r']
>>> list(itertools.islice('Aardvark', 1, 7, 2))
['a', 'd', 'a']
用于映射的生成器函数
在输入的单个可迭代对象(map 和starmap 函数处理多个可迭代的对象)中的各个元素上做计算,然后返回结果
模块 | 函数 | 说明 |
itertools |
accumulate(it, |
产出累积的总和;如果提供了 func ,那么把前两个 |
内置 |
enumerate(iterable, |
产出由两个元素组成的元组,结构是 (index, |
内置 |
map(func, it1, |
把 it 中的各个元素传给func,产出结果;如果传入 |
itertools | starmap(func, it) |
把 it 中的各个元素传给 func ,产出结果;输入的 |
#演示 itertools.accumulate 生成器函数
>>> sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
>>> import itertools
>>> list(itertools.accumulate(sample)) # ➊
[5, 9, 11, 19, 26, 32, 35, 35, 44, 45]
12
12
>>> list(itertools.accumulate(sample, min)) # ➋
[5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
>>> list(itertools.accumulate(sample, max)) # ➌
[5, 5, 5, 8, 8, 8, 8, 8, 9, 9]
>>> import operator
>>> list(itertools.accumulate(sample, operator.mul)) # ➍
[5, 20, 40, 320, 2240, 13440, 40320, 0, 0, 0]
>>> list(itertools.accumulate(range(1, 11), operator.mul))
[1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800] # ➎
❶ 计算总和。
❷ 计算最小值。
❸ 计算最大值。
❹ 计算乘积。
❺ 从 1! 到 10!,计算各个数的阶乘。
演示用于映射的生成器函数
>>> list(enumerate('albatroz', 1)) # ➊
[(1, 'a'), (2, 'l'), (3, 'b'), (4, 'a'), (5, 't'), (6, 'r'), (7, 'o'), (8, 'z')]
>>> import operator
>>> list(map(operator.mul, range(11), range(11))) # ➋
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> list(map(operator.mul, range(11), [2, 4, 8])) # ➌
[0, 4, 16]
>>> list(map(lambda a, b: (a, b), range(11), [2, 4, 8])) # ➍
[(0, 2), (1, 4), (2, 8)]
>>> import itertools
>>> list(itertools.starmap(operator.mul, enumerate('albatroz', 1))) # ➎
['a', 'll', 'bbb', 'aaaa', 'ttttt', 'rrrrrr', 'ooooooo', 'zzzzzzzz']
>>> sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
>>> list(itertools.starmap(lambda a, b: b/a,
... enumerate(itertools.accumulate(sample), 1))) # ➏
[5.0, 4.5, 3.6666666666666665, 4.75, 5.2, 5.333333333333333,
5.0, 4.375, 4.888888888888889, 4.5]
❶ 从 1 开始,为单词中的字母编号。
❷ 从 0 到 10,计算各个整数的平方。
❸ 计算两个可迭代对象中对应位置上的两个元素之积,元素最少的那个可迭代对象到头后就停止。
❹ 作用等同于内置的 zip 函数。
❺ 从 1 开始,根据字母所在的位置,把字母重复相应的次数。
❻ 计算平均值。
合并多个可迭代对象的生成器函数
模块 | 函数 | 说明 |
itertools | chain(it1, ..., itN) |
先产出 it1 中的所有元素,然后产出 it2 中的 |
itertools | chain.from_iterable(it) |
产出 it 生成的各个可迭代对象中的元素,一个 |
itertools |
product(it1, ..., itN, |
计算笛卡儿积:从输入的各个可迭代对象中获 |
内置 | zip(it1, ..., itN) |
并行从输入的各个可迭代对象中获取元素,产 |
itertools |
zip_longest(it1, ..., |
并行从输入的各个可迭代对象中获取元素,产 |
演示用于合并的生成器函数
>>> list(itertools.chain('ABC', range(2))) # ➊
['A', 'B', 'C', 0, 1]
>>> list(itertools.chain(enumerate('ABC'))) # ➋
[(0, 'A'), (1, 'B'), (2, 'C')]
>>> list(itertools.chain.from_iterable(enumerate('ABC'))) # ➌
[0, 'A', 1, 'B', 2, 'C']
>>> list(zip('ABC', range(5))) # ➍
[('A', 0), ('B', 1), ('C', 2)]
>>> list(zip('ABC', range(5), [10, 20, 30, 40])) # ➎
[('A', 0, 10), ('B', 1, 20), ('C', 2, 30)]
>>> list(itertools.zip_longest('ABC', range(5))) # ➏
[('A', 0), ('B', 1), ('C', 2), (None, 3), (None, 4)]
>>> list(itertools.zip_longest('ABC', range(5), fillvalue='?')) # ➐
[('A', 0), ('B', 1), ('C', 2), ('?', 3), ('?', 4)]
❶ 调用 chain 函数时通常传入两个或更多个可迭代对象。
❷ 如果只传入一个可迭代的对象,那么 chain 函数没什么用。
❸ 但是 chain.from_iterable 函数从可迭代的对象中获取每个元素,
然后按顺序把元素连接起来,前提是各个元素本身也是可迭代的对象。
❹ zip 常用于把两个可迭代的对象合并成一系列由两个元素组成的元
组。
❺ zip 可以并行处理任意数量个可迭代的对象,不过只要有一个可迭代
的对象到头了,生成器就停止。
❻ itertools.zip_longest 函数的作用与 zip 类似,不过输入的所有
可迭代对象都会处理到头,如果需要会填充 None。
❼ fillvalue 关键字参数用于指定填充的值。
itertools.product 生成器是计算笛卡儿积的惰性方式;以惰性方式计算笛卡儿积。
示例itertools.product 函数的用法。
>>> list(itertools.product('ABC', range(2))) # ➊
[('A', 0), ('A', 1), ('B', 0), ('B', 1), ('C', 0), ('C', 1)]
>>> suits = 'spades hearts diamonds clubs'.split()
>>> list(itertools.product('AK', suits)) # ➋
[('A', 'spades'), ('A', 'hearts'), ('A', 'diamonds'), ('A', 'clubs'),
('K', 'spades'), ('K', 'hearts'), ('K', 'diamonds'), ('K', 'clubs')]
>>> list(itertools.product('ABC')) # ➌
[('A',), ('B',), ('C',)]
>>> list(itertools.product('ABC', repeat=2)) # ➍
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'),
('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]
>>> list(itertools.product(range(2), repeat=3))
[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0),
(1, 0, 1), (1, 1, 0), (1, 1, 1)]
>>> rows = itertools.product('AB', range(2), repeat=2)
>>> for row in rows: print(row)
...
('A', 0, 'A', 0)
('A', 0, 'A', 1)
('A', 0, 'B', 0)
('A', 0, 'B', 1)
('A', 1, 'A', 0)
('A', 1, 'A', 1)
('A', 1, 'B', 0)
('A', 1, 'B', 1)
('B', 0, 'A', 0)
('B', 0, 'A', 1)
('B', 0, 'B', 0)
('B', 0, 'B', 1)
('B', 1, 'A', 0)
('B', 1, 'A', 1)
('B', 1, 'B', 0)
('B', 1, 'B', 1)
❶ 三个字符的字符串与两个整数的值域得到的笛卡儿积是六个元组
(因为 3 * 2 等于 6)。
❷ 两张牌('AK')与四种花色得到的笛卡儿积是八个元组。
❸ 如果传入一个可迭代的对象,product 函数产出的是一系列只有一
个元素的元组,不是特别有用。
❹ repeat=N 关键字参数告诉 product 函数重复 N 次处理输入的各个
可迭代对象。
把输入的各个元素扩展成多个输出元素的生成器函数
模块 | 函数 | 说明 |
itertools | combinations(it, out_len) |
把 it 产出的 out_len 个元素组合在 |
itertools |
combinations_with_replacement(it, |
把 it 产出的 out_len 个元素组合在 |
itertools | count(start=0, step=1) |
从 start 开始不断产出数字,按 |
itertools | cycle(it) |
从 it 中产出各个元素,存储各个元 |
itertools | permutations(it, out_len=None) |
把 out_len 个 it 产出的元素排列在 |
itertools | repeat(item, [times]) |
重复不断地产出指定的元素,除非 |
itertools 模块中的 count 和 repeat 函数返回的生成器“无中生有”:这两个函数都不接受可迭代的对象作为输入。
cycle 生成器会备份输入的可迭代对象,然后重复产出对象中的元素。
count、repeat 和 cycle的用法。
>>> ct = itertools.count() # ➊
>>> next(ct) # ➋
0
>>> next(ct), next(ct), next(ct) # ➌
(1, 2, 3)
>>> list(itertools.islice(itertools.count(1, .3), 3)) # ➍
[1, 1.3, 1.6]
>>> cy = itertools.cycle('ABC') # ➎
>>> next(cy)
'A'
>>> list(itertools.islice(cy, 7)) # ➏
['B', 'C', 'A', 'B', 'C', 'A', 'B']
>>> rp = itertools.repeat(7) # ➐
>>> next(rp), next(rp)
(7, 7)
>>> list(itertools.repeat(8, 4)) # ➑
[8, 8, 8, 8]
>>> list(map(operator.mul, range(11), itertools.repeat(5))) # ➒
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
❶ 使用 count 函数构建 ct 生成器。
❷ 获取 ct 中的第一个元素。
❸ 不能使用 ct 构建列表,因为 ct 是无穷的,所以我获取接下来的 3
个元素。
❹ 如果使用 islice 或 takewhile 函数做了限制,可以从 count 生成
器中构建列表。
❺ 使用 'ABC' 构建一个 cycle 生成器,然后获取第一个元素
——'A'。
❻ 只有受到 islice 函数的限制,才能构建列表;这里获取接下来的 7
个元素。
❼ 构建一个 repeat 生成器,始终产出数字 7。
❽ 传入 times 参数可以限制 repeat 生成器生成的元素数量:这里会
生成 4 次数字 8。
❾ repeat 函数的常见用途:为 map 函数提供固定参数,这里提供的是
乘数 5。
组合学生成器函数会从输入的各个元素中产出多个值
>>> list(itertools.combinations('ABC', 2)) # ➊
[('A', 'B'), ('A', 'C'), ('B', 'C')]
>>> list(itertools.combinations_with_replacement('ABC', 2)) # ➋
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
>>> list(itertools.permutations('ABC', 2)) # ➌
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
>>> list(itertools.product('ABC', repeat=2)) # ➍
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'),
('C', 'A'), ('C', 'B'), ('C', 'C')]
❶ 'ABC' 中每两个元素(len()==2)的各种组合;在生成的元组中,
元素的顺序无关紧要(可以视作集合)。
❷ 'ABC' 中每两个元素(len()==2)的各种组合,包括相同元素的组
合。
❸ 'ABC' 中每两个元素(len()==2)的各种排列;在生成的元组中,
元素的顺序有重要意义。
❹ 'ABC' 和 'ABC'(repeat=2 的效果)的笛卡儿积。
用于重新排列元素的生成器函数
模块 | 函数 | 说明 |
itertools | groupby(it,key=None) |
产出由两个元素组成的元素,形式为 (key, |
内置 | reversed(seq) |
从后向前,倒序产出 seq 中的元素; seq 必须是序 |
itertools | tee(it, n=2) |
产出一个由 n 个生成器组成的元组,每个生成器 |
itertools.groupby 函数的用法
>>> list(itertools.groupby('LLLLAAGGG')) # ➊
[('L', <itertools._grouper object at 0x102227cc0>),
('A', <itertools._grouper object at 0x102227b38>),
('G', <itertools._grouper object at 0x102227b70>)]
>>> for char, group in itertools.groupby('LLLLAAAGG'): # ➋
... print(char, '->', list(group))
...
L -> ['L', 'L', 'L', 'L']
A -> ['A', 'A',]
G -> ['G', 'G', 'G']
>>> animals = ['duck', 'eagle', 'rat', 'giraffe', 'bear',
... 'bat', 'dolphin', 'shark', 'lion']
>>> animals.sort(key=len) # ➌
>>> animals
['rat', 'bat', 'duck', 'bear', 'lion', 'eagle', 'shark',
'giraffe', 'dolphin']
>>> for length, group in itertools.groupby(animals, len): # ➍
... print(length, '->', list(group))
...
3 -> ['rat', 'bat']
4 -> ['duck', 'bear', 'lion']
5 -> ['eagle', 'shark']
7 -> ['giraffe', 'dolphin']
>>> for length, group in itertools.groupby(reversed(animals), len): # ➎
... print(length, '->', list(group))
...
7 -> ['dolphin', 'giraffe']
5 -> ['shark', 'eagle']
4 -> ['lion', 'bear', 'duck']
3 -> ['bat', 'rat']
>>>
❶ groupby 函数产出 (key, group_generator) 这种形式的元组。
❷ 处理 groupby 函数返回的生成器要嵌套迭代:这里在外层使用 for
循环,内层使用列表推导。
❸ 为了使用 groupby 函数,要排序输入;这里按照单词的长度排序。
❹ 再次遍历 key 和 group 值对,把 key 显示出来,并把 group 扩展成
列表。
❺ 这里使用 reverse 生成器从右向左迭代 animals。
最后一个生成器函数是 iterator.tee,这个函数只有一个
作用:从输入的一个可迭代对象中产出多个生成器,每个生成器都可以产出输入的各个元素。产出的生成器可以单独使用
>>> list(itertools.tee('ABC'))
[<itertools._tee object at 0x10222abc8>, <itertools._tee object at 0x10222ac08>]
>>> g1, g2 = itertools.tee('ABC')
>>> next(g1)
'A'
>>> next(g2)
'A'
>>> next(g2)
'B'
>>> list(g1)
['B', 'C']
>>> list(g2)
['C']
>>> list(zip(*itertools.tee('ABC')))
[('A', 'A'), ('B', 'B'), ('C', 'C')]
Python 3.3中新出现的句法:yield from
如果生成器函数需要产出另一个生成器生成的值,传统的解决方法是使用嵌套的 for 循环。
>>> def chain(*iterables):
... for it in iterables:
... for i in it:
... yield i
...
>>> s = 'ABC'
>>> t = tuple(range(3))
>>> list(chain(s, t))
['A', 'B', 'C', 0, 1, 2]
>>> def chain(*iterables):
... for i in iterables:
... yield from i
...
>>> list(chain(s, t))
['A', 'B', 'C', 0, 1, 2]
可以看出,yield from i 完全代替了内层的 for 循环。在这个示例中使用 yield from 是对的,而且代码读起来更顺畅,不过感觉更像是语法糖。
除了代替循环之外,yield from 还会创建通道,把内层生成器直接与外层生成器的客户端联系起来。把生成器当成协程使用时,这个通道特别重要,不仅能为客户端代码生成值,还能使用客户端代码提供的值。
流畅的python 14章可迭代的对象、迭代器 和生成器的更多相关文章
- Python核心编程的四大神兽:迭代器、生成器、闭包以及装饰器
生成器 生成器是生成一个值的特殊函数,它具有这样的特点:第一次执行该函数时,先从头按顺序执行,在碰到yield关键字时该函数会暂停执行该函数后续的代码,并且返回一个值:在下一次调用该函数执行时,程 ...
- 流畅的python第十四章可迭代的对象,迭代器和生成器学习记录
在python中,所有集合都可以迭代,在python语言内部,迭代器用于支持 for循环 构建和扩展集合类型 逐行遍历文本文件 列表推导,字典推导和集合推导 元组拆包 调用函数时,使用*拆包实参 本章 ...
- 流畅的python第二章序列构成的数组学习记录
python内置序列类型概览 列表推导和生成器表达式 列表推导是构建列表的快捷方式,而生成器表达式可以用来创建其他任何类型的序列 列表推导的示例 >>>test = [i*2 for ...
- python编程系列---可迭代对象,迭代器和生成器详解
一.三者在代码上的特征 1.有__iter__方法的对象就是可迭代类(对象) 2.有__iter__方法,__next()方法的对象就是迭代器3.生成器 == 函数+yield 生成器属于迭代器, 迭 ...
- Python开发【第五篇】迭代器、生成器、递归函数、二分法
阅读目录 一.迭代器 1. 迭代的概念 #迭代器即迭代的工具(自定义的函数),那什么是迭代呢? #迭代:指一个重复的过程,每次重复都可以称之为一次迭代,并且每一次重复的结果是下一个迭代的初始值(例如: ...
- Python 入门基础11 --函数基础4 迭代器、生成器、枚举类型
今日目录: 1.迭代器 2.可迭代对象 3.迭代器对象 4.for循环迭代器 5.生成器 6.枚举对象 一.迭代器: 循环反馈的容器(集合类型) 每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的 ...
- Python——五分钟带你弄懂迭代器与生成器,夯实代码能力
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是周一Python专题,给大家带来的是Python当中生成器和迭代器的使用. 我当初第一次学到迭代器和生成器的时候,并没有太在意,只是觉 ...
- [流畅的Python]第一章数据模型
这些来自同一家出版社的动物书 像是计算机科学界一盏盏指路明灯 余幼时 初试读 学浅 以为之晦涩难懂 像是老学究咬文嚼字 现在看起来还有些有趣 其实理工男大多都很有趣 这一章介绍了 怎么样去视线一个带有 ...
- Head First Python 1-4章学习感悟
一.学习知识总结 (1)迭代 range(起始值,结束值,步长):包含起始值,不包含结束值,步长为正数 (2)导入模块 from datetime import datetime (3 ...
随机推荐
- iptables详细教程:基础、架构、清空规则、追加规则、应用实例(转)
iptables防火墙可以用于创建过滤(filter)与NAT规则.所有Linux发行版都能使用iptables,因此理解如何配置iptables将会帮助你更有效地管理Linux防火墙.如果你是第一次 ...
- JVM-String常量池与运行时常量池
Start with JVM 周志明先生著-<深入理解Java虚拟机>,书买回来好几天了,但是最近才准备开始搞一搞了(哭瞎…..).首先是第一章的Java以及JVM发展历史,大概知道了现行 ...
- oozie搭建手册
环境准备 mave安装 1.下载并上传maven,然后解压 tar -zxvf apache-maven-3.3.9-bin.tar.gz -C /home 2.配置环境变量 vi /etc/prof ...
- TCP/IP详解读书笔记:ARP-地址解析协议
地址解析为两种不同的地址形式提供映射:32bit的IP和数据链路层使用的任何类型的地址. 当一台主机把以太网数据帧发送到位于同一局域网的另一台主机,是根据48bit的以太网地址而不是IP地址.设备驱动 ...
- hihoCoder #1320 : 压缩字符串 区间dp
/** 题目:hihoCoder #1320 : 压缩字符串 链接:https://hihocoder.com/problemset/problem/1320 描述 小Hi希望压缩一个只包含大写字母' ...
- [转]Linux系统下yum和apt-get的区别
一般来说著名的linux系统基本上分两大类: 1.RedHat系列:Redhat.Centos.Fedora等 2.Debian系列:Debian.Ubuntu等 RedHat 系列 1 常见的安装包 ...
- tomcat 内存溢出原因分析及解决
一.错误提示:java.lang.OutOfMemoryError: Java heap space [原因分析] tomcat默认可以使用内存为128MB,在较大型的应用项目中不足以满足运行要求,在 ...
- python通过日志分析加入黑名单
监控nginx日志,若有人攻击,则加入黑名单,操作步骤如下:1.读取日志文件2.分隔文件,取出ip3.将取出的ip放入list,然后判读ip的次数4.若超过设定的次数,则加入黑名单 日志信息如下: 1 ...
- 第二百三十八节,Bootstrap输入框和导航组件
Bootstrap输入框和导航组件 学习要点: 1.输入框组件 2.导航组件 3.导航条组件 本节课我们主要学习一下Bootstrap的两个个组件功能:输入框组件和导航导航条组件. 一.输入框组件 文 ...
- [翻译]在ASP.NET Web API中通过OData支持查询和分页
OData可以通过形如http://localhost/Products?$orderby=Name这样的QueryString传递查询条件.排序等.你可以在任何Web API Controller中 ...