流畅的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 ...
随机推荐
- Python 集合常用方法总结
数据类型:int/str/bool/list/dict/tuple/float/set (set类型天生去重) 一.集合的定义 s = set() #定义空集合 s = {'a','b','c' ...
- myeclipce怎么破解
MyEclipse安装文件下载,下载地址 http://www.jb51.net/softs/150886.html 你也可以进入官方网站下载:http://www.myeclipsecn.com/d ...
- sitemesh 2.4 装饰器学习
SiteMesh 是一个网页布局和修饰的框架,利用它可以将网页的内容和页面结构分离,以达到页面结构共享的目的 SiteMesh是OpenSymphony团队开发的JEE框架之一,它是一个非常优秀的页面 ...
- phpcms 初次建站心得
最近要给客户建个网站,考虑到效率问题,直接找了个开源的phpcms,(现在被收购了,以前的时候我还知道是个开源的).由于对这个东西不熟悉,原来就是了解一些,php的建站系统,php的MVC框架.故此, ...
- ES6快速入门使用
https://www.jianshu.com/p/061304806bda Babel-webpack的配置 Bebal官方文档 前段项目我想使用ES6的语法我应该怎么做呢?我们可以查看Babel的 ...
- C0302 将一个代码块中的内容保存在文件中, 查看一个rpm包是否可以安装
#!/bin/bash # 这个脚本是用来描述和确认是否可以安装一个rpm包 # 在一个文件中保存输出 SUCCESS=0 E_NOARGS=65 if [ -z "$1" ] t ...
- C++ 类的多态三(多态的原理--虚函数指针--子类虚函数指针初始化)
//多态的原理--虚函数指针--子类虚函数指针初始化 #include<iostream> using namespace std; /* 多态的实现原理(有自己猜想部分) 基础知识: 类 ...
- C++ 类的继承五(类继承中的static关键字)
//类继承中的static关键字 #include<iostream> using namespace std; /* 派生类中的静态成员 基类定义的静态成员,将被所有派生类共享 根据静态 ...
- php 怎么查看是否开启了socket
<?php if(extension_loaded('sockets')){ echo "已开启"; }else{ echo "未开启"; } ?>
- MAC Ruby版本需要升级至2.2.2以上
第一例: 默认情况下,Mac OS X 系统已经安装好 Ruby(最新的 Mavericks 随机的 Ruby 版本为 2.0.0p247),安装在 /System/Library/Framework ...