可迭代的对象、迭代器和生成器

迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。

迭代器用于从集合中取出元素;而生成器用于“凭空”生成元素。通过斐波纳契数列能很好地说明二者之间的区别:斐波纳契数
列中的数有无穷个,在一个集合里放不下。不过要知道,在 Python社区中,大多数时候都把迭代器和生成器视作同一概念。

Sentence类

单词序列

  1. import re
  2. import reprlib
  3. RE_WORD = re.compile('\w+')
  4. class Sentence:
  5. def __init__(self, text):
  6. self.text = text
  7. self.words = RE_WORD.findall(text)
  8. def __getitem__(self, index):
  9. return self.words[index]
  10. def __len__(self):
  11. return len(self.words)
  12. def __repr__(self):
  13. return 'Sentence(%s)' % reprlib.repr(self.text)

❶re.findall 函数返回一个字符串列表,里面的元素是正则表达式的
全部非重叠匹配。
❷ self.words 中保存的是 .findall 函数返回的结果,因此直接返回
指定索引位上的单词。
❸ 为了完善序列协议,我们实现了 __len__ 方法;不过,为了让对象
可以迭代,没必要实现这个方法。
❹ reprlib.repr 这个实用函数用于生成大型数据结构的简略字符串表
示形式。

示例 14-2 测试 Sentence 实例能否迭代

  1. >>> s = Sentence('"The time has come," the Walrus said,') # ➊
  2. >>> s
  3. Sentence('"The time ha... Walrus said,') # ➋
  4. >>> for word in s: # ➌
  5. ... print(word)
  6. The
  7. time
  8. has
  9. come
  10. the
  11. Walrus
  12. said
  13. >>> list(s) # ➍
  14. ['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'是可迭代的对象。背后是有迭代器的,只不过我们看不到:

  1. >>> s = 'ABC'
  2. >>> for char in s:
  3. ... print(char)
  4. ...
  5. A
  6. B
  7. C
  8. ###如果没有 for 语句,不得不使用 while 循环模拟,要像下面这样写:
  9. >>> s = 'ABC'
  10. >>> it = iter(s) # ➊
  11. >>> while True:
  12. ... try:
  13. ... print(next(it)) # ➋
  14. ... except StopIteration: # ➌
  15. ... del it # ➍
  16. ... break # ➎
  17. ...
  18. A
  19. B
  20. C

❶ 使用可迭代的对象构建迭代器 it。
❷ 不断在迭代器上调用 next 函数,获取下一个字符。
❸ 如果没有字符了,迭代器会抛出 StopIteration 异常。
❹ 释放对 it 的引用,即废弃迭代器对象。
❺ 退出循环。

StopIteration 异常表明迭代器到头了。Python 语言内部会处理 for循环和其他迭代上下文(如列表推导、元组拆包,等等)中的StopIteration 异常。

标准的迭代器接口有两个方法。
__next__
  返回下一个可用的元素,如果没有元素了,抛出 StopIteration异常。
__iter__
  返回 self,以便在应该使用可迭代对象的地方使用迭代器,例如在 for 循环中。

迭代器

迭代器是这样的对象:实现了无参数的 __next__ 方法,返回序列中的下一个元素;如果没有元素了,那么抛出 StopIteration 异常。Python 中的迭代器还实现了 __iter__ 方法,因此迭代器也可以迭代。

典型的迭代器

使用迭代器模式实现 Sentence 类

  1. import re
  2. import reprlib
  3. RE_WORD = re.compile('\w+')
  4. class Sentence:
  5. def __init__(self, text):
  6. self.text = text
  7. self.words = RE_WORD.findall(text)
  8. def __repr__(self):
  9. return 'Sentence(%s)' % reprlib.repr(self.text)
  10. def __iter__(self): #与前一版相比,这里只多了一个 __iter__ 方法。这一版没有
  11. #__getitem__ 方法,为的是明确表明这个类可以迭代,因为实现了
  12. #__iter__ 方法。
  13. return SentenceIterator(self.words) #根据可迭代协议,__iter__ 方法实例化并返回一个迭代器。
  14. class SentenceIterator:
  15. def __init__(self, words):
  16. self.words = words #SentenceIterator 实例引用单词列表。
  17. self.index = 0#self.index 用于确定下一个要获取的单词。
  18. def __next__(self):
  19. try:
  20. word = self.words[self.index] #获取 self.index 索引位上的单词。
  21. except IndexError:
  22. raise StopIteration() #如果 self.index 索引位上没有单词,那么抛出 StopIteration 异
  23. 常。
  24. self.index += 1
  25. return word
  26. def __iter__(self): #实现 self.__iter__ 方法。
  27. return self

生成器函数

  1. import re
  2. import reprlib
  3. RE_WORD = re.compile('\w+')
  4. class Sentence:
  5. def __init__(self, text):
  6. self.text = text
  7. self.words = RE_WORD.findall(text)
  8. def __repr__(self):
  9. return 'Sentence(%s)' % reprlib.repr(self.text)
  10. def __iter__(self):
  11. for word in self.words:
  12. yield word
  13. 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 类

  1. import re
  2. import reprlib
  3. RE_WORD = re.compile('\w+')
  4. class Sentence:
  5. def __init__(self, text):
  6. self.text = text
  7. def __repr__(self):
  8. return 'Sentence(%s)' % reprlib.repr(self.text)
  9. def __iter__(self):
  10. for match in RE_WORD.finditer(self.text):
  11. yield match.group()

❶ 不再需要 words 列表。
❷ finditer 函数构建一个迭代器,包含 self.text 中匹配 RE_WORD
的单词,产出 MatchObject 实例。
❸ match.group() 方法从 MatchObject 实例中提取匹配正则表达式的
具体文本。
生成器函数已经极大地简化了代码,但是使用生成器表达式甚至能把代
码变得更简短。

生成器表达式

生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。也就是说,如果列表推导是制造列表的工厂,那么生成器表达式就是制造生成器的工厂。

  1. >>> def gen_AB(): # ➊
  2. ... print('start')
  3. ... yield 'A'
  4. ... print('continue')
  5. ... yield 'B'
  6. ... print('end.')
  7. ...
  8. >>> res1 = [x*3 for x in gen_AB()] # ➋
  9. start
  10. continue
  11. end.
  12. >>> for i in res1: # ➌
  13. ... print('-->', i)
  14. ...
  15. --> AAA
  16. --> BBB
  17. >>> res2 = (x*3 for x in gen_AB()) # ➍
  18. >>> res2 # ➎
  19. <generator object <genexpr> at 0x10063c240>
  20. >>> for i in res2: # ➏
  21. ... print('-->', i)
  22. ...
  23. start
  24. --> AAA
  25. continue
  26. --> BBB
  27. 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类

  1. import re
  2. import reprlib
  3. RE_WORD = re.compile('\w+')
  4. class Sentence:
  5. def __init__(self, text):
  6. self.text = text
  7. def __repr__(self):
  8. return 'Sentence(%s)' % reprlib.repr(self.text)
  9. def __iter__(self):
  10. return (match.group() for match in RE_WORD.finditer(self.text))

这里不是生成器函数了(没有 yield),而是使用生成器表达式构建生成器,然后将其返回。不过,最终的效果一样:调用 __iter__ 方法会得到一个生成器对象。
生成器表达式是语法糖:完全可以替换成生成器函数,不过有时使用生成器表达式更便利。

标准库中的生成器函数

用于过滤的生成器函数

模块 函数 说明
itertools

compress(it,
selector_it)

并行处理两个可迭代的对象;如果 selector_it
中的元素是真值,产出 it 中对应的元素

itertools

dropwhile(predicate,
it)

处理 it ,跳过 predicate 的计算结果为真值的元
素,然后产出剩下的各个元素(不再进一步检
查)

内置 filter(predicate, it)

把 it 中的各个元素传给 predicate ,如果
predicate(item) 返回真值,那么产出对应的元
素;如果 predicate 是 None ,那么只产出真值元

itertools

filterfalse(predicate,
it)

与 filter 函数的作用类似,不过 predicate 的
逻辑是相反的: predicate 返回假值时产出对应
的元素

itertools

islice(it, stop) 或
islice(it, start,
stop, step=1)

产出 it 的切片,作用类似于 s[:stop] 或
s[start:stop:step] ,不过 it 可以是任何可迭代
的对象,而且这个函数实现的是惰性操作

itertools

takewhile(predicate,
it)

predicate 返回真值时产出对应的元素,然后立
即停止,不再继续检查

  1. >>> def vowel(c):
  2. ... return c.lower() in 'aeiou'
  3. ...
  4. >>> list(filter(vowel, 'Aardvark'))
  5. ['A', 'a', 'a']
  6. >>> import itertools
  7. >>> list(itertools.filterfalse(vowel, 'Aardvark'))
  8. ['r', 'd', 'v', 'r', 'k']
  9. >>> list(itertools.dropwhile(vowel, 'Aardvark'))
  10. ['r', 'd', 'v', 'a', 'r', 'k']
  11. >>> list(itertools.takewhile(vowel, 'Aardvark'))
  12. ['A', 'a']
  13. >>> list(itertools.compress('Aardvark', (1,0,1,1,0,1)))
  14. ['A', 'r', 'd', 'a']
  15. >>> list(itertools.islice('Aardvark', 4))
  16. ['A', 'a', 'r', 'd']
  17. >>> list(itertools.islice('Aardvark', 4, 7))
  18. ['v', 'a', 'r']
  19. >>> list(itertools.islice('Aardvark', 1, 7, 2))
  20. ['a', 'd', 'a']

用于映射的生成器函数

在输入的单个可迭代对象(map 和starmap 函数处理多个可迭代的对象)中的各个元素上做计算,然后返回结果

模块 函数 说明
itertools

accumulate(it,
[func])

产出累积的总和;如果提供了 func ,那么把前两个
元素传给它,然后把计算结果和下一个元素传给
它,以此类推,最后产出结果

内置

enumerate(iterable,
start=0)

产出由两个元素组成的元组,结构是 (index,
item) ,其中 index 从 start 开始计数, item 则从
iterable 中获取

内置

map(func, it1,
[it2, ..., itN])

把 it 中的各个元素传给func,产出结果;如果传入
N 个可迭代的对象,那么 func 必须能接受 N 个参
数,而且要并行处理各个可迭代的对象

itertools starmap(func, it)

把 it 中的各个元素传给 func ,产出结果;输入的
可迭代对象应该产出可迭代的元素 iit ,然后以
func(*iit) 这种形式调用 func

  1. #演示 itertools.accumulate 生成器函数
  2. >>> sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
  3. >>> import itertools
  4. >>> list(itertools.accumulate(sample)) # ➊
  5. [5, 9, 11, 19, 26, 32, 35, 35, 44, 45]
  6. 12
  7. 12
  8. >>> list(itertools.accumulate(sample, min)) # ➋
  9. [5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
  10. >>> list(itertools.accumulate(sample, max)) # ➌
  11. [5, 5, 5, 8, 8, 8, 8, 8, 9, 9]
  12. >>> import operator
  13. >>> list(itertools.accumulate(sample, operator.mul)) # ➍
  14. [5, 20, 40, 320, 2240, 13440, 40320, 0, 0, 0]
  15. >>> list(itertools.accumulate(range(1, 11), operator.mul))
  16. [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800] # ➎

❶ 计算总和。
❷ 计算最小值。
❸ 计算最大值。
❹ 计算乘积。
❺ 从 1! 到 10!,计算各个数的阶乘。

演示用于映射的生成器函数

  1. >>> list(enumerate('albatroz', 1)) # ➊
  2. [(1, 'a'), (2, 'l'), (3, 'b'), (4, 'a'), (5, 't'), (6, 'r'), (7, 'o'), (8, 'z')]
  3. >>> import operator
  4. >>> list(map(operator.mul, range(11), range(11))) # ➋
  5. [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  6. >>> list(map(operator.mul, range(11), [2, 4, 8])) # ➌
  7. [0, 4, 16]
  8. >>> list(map(lambda a, b: (a, b), range(11), [2, 4, 8])) # ➍
  9. [(0, 2), (1, 4), (2, 8)]
  10. >>> import itertools
  11. >>> list(itertools.starmap(operator.mul, enumerate('albatroz', 1))) # ➎
  12. ['a', 'll', 'bbb', 'aaaa', 'ttttt', 'rrrrrr', 'ooooooo', 'zzzzzzzz']
  13. >>> sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
  14. >>> list(itertools.starmap(lambda a, b: b/a,
  15. ... enumerate(itertools.accumulate(sample), 1))) # ➏
  16. [5.0, 4.5, 3.6666666666666665, 4.75, 5.2, 5.333333333333333,
  17. 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 生成的各个可迭代对象中的元素,一个
接一个,无缝连接在一起; it 应该产出可迭代
的元素,例如可迭代的对象列表

itertools

product(it1, ..., itN,
repeat=1)

计算笛卡儿积:从输入的各个可迭代对象中获
取元素,合并成由 N 个元素组成的元组,与嵌
套的 for 循环效果一样; repeat 指明重复处理
多少次输入的可迭代对象

内置 zip(it1, ..., itN)

并行从输入的各个可迭代对象中获取元素,产
出由 N 个元素组成的元组,只要有一个可迭代
的对象到头了,就默默地停止

itertools

zip_longest(it1, ...,
itN, fillvalue=None)

并行从输入的各个可迭代对象中获取元素,产
出由 N 个元素组成的元组,等到最长的可迭代
对象到头后才停止,空缺的值使用 fillvalue
填充

演示用于合并的生成器函数

  1. >>> list(itertools.chain('ABC', range(2))) # ➊
  2. ['A', 'B', 'C', 0, 1]
  3. >>> list(itertools.chain(enumerate('ABC'))) # ➋
  4. [(0, 'A'), (1, 'B'), (2, 'C')]
  5. >>> list(itertools.chain.from_iterable(enumerate('ABC'))) # ➌
  6. [0, 'A', 1, 'B', 2, 'C']
  7. >>> list(zip('ABC', range(5))) # ➍
  8. [('A', 0), ('B', 1), ('C', 2)]
  9. >>> list(zip('ABC', range(5), [10, 20, 30, 40])) # ➎
  10. [('A', 0, 10), ('B', 1, 20), ('C', 2, 30)]
  11. >>> list(itertools.zip_longest('ABC', range(5))) # ➏
  12. [('A', 0), ('B', 1), ('C', 2), (None, 3), (None, 4)]
  13. >>> list(itertools.zip_longest('ABC', range(5), fillvalue='?')) # ➐
  14. [('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 函数的用法。

  1. >>> list(itertools.product('ABC', range(2))) # ➊
  2. [('A', 0), ('A', 1), ('B', 0), ('B', 1), ('C', 0), ('C', 1)]
  3. >>> suits = 'spades hearts diamonds clubs'.split()
  4. >>> list(itertools.product('AK', suits)) # ➋
  5. [('A', 'spades'), ('A', 'hearts'), ('A', 'diamonds'), ('A', 'clubs'),
  6. ('K', 'spades'), ('K', 'hearts'), ('K', 'diamonds'), ('K', 'clubs')]
  7. >>> list(itertools.product('ABC')) # ➌
  8. [('A',), ('B',), ('C',)]
  9. >>> list(itertools.product('ABC', repeat=2)) # ➍
  10. [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'),
  11. ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]
  12. >>> list(itertools.product(range(2), repeat=3))
  13. [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0),
  14. (1, 0, 1), (1, 1, 0), (1, 1, 1)]
  15. >>> rows = itertools.product('AB', range(2), repeat=2)
  16. >>> for row in rows: print(row)
  17. ...
  18. ('A', 0, 'A', 0)
  19. ('A', 0, 'A', 1)
  20. ('A', 0, 'B', 0)
  21. ('A', 0, 'B', 1)
  22. ('A', 1, 'A', 0)
  23. ('A', 1, 'A', 1)
  24. ('A', 1, 'B', 0)
  25. ('A', 1, 'B', 1)
  26. ('B', 0, 'A', 0)
  27. ('B', 0, 'A', 1)
  28. ('B', 0, 'B', 0)
  29. ('B', 0, 'B', 1)
  30. ('B', 1, 'A', 0)
  31. ('B', 1, 'A', 1)
  32. ('B', 1, 'B', 0)
  33. ('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,
out_len)

把 it 产出的 out_len 个元素组合在
一起,然后产出,包含相同元素的
组合

itertools count(start=0, step=1)

从 start 开始不断产出数字,按
step 指定的步幅增加

itertools cycle(it)

从 it 中产出各个元素,存储各个元
素的副本,然后按顺序重复不断地
产出各个元素

itertools permutations(it, out_len=None)

把 out_len 个 it 产出的元素排列在
一起,然后产出这些排列; out_len
的默认值等于 len(list(it))

itertools repeat(item, [times])

重复不断地产出指定的元素,除非
提供 times ,指定次数

itertools 模块中的 count 和 repeat 函数返回的生成器“无中生有”:这两个函数都不接受可迭代的对象作为输入。
cycle 生成器会备份输入的可迭代对象,然后重复产出对象中的元素。

count、repeat 和 cycle的用法。

  1. >>> ct = itertools.count() # ➊
  2. >>> next(ct) # ➋
  3. 0
  4. >>> next(ct), next(ct), next(ct) # ➌
  5. (1, 2, 3)
  6. >>> list(itertools.islice(itertools.count(1, .3), 3)) # ➍
  7. [1, 1.3, 1.6]
  8. >>> cy = itertools.cycle('ABC') # ➎
  9. >>> next(cy)
  10. 'A'
  11. >>> list(itertools.islice(cy, 7)) # ➏
  12. ['B', 'C', 'A', 'B', 'C', 'A', 'B']
  13. >>> rp = itertools.repeat(7) # ➐
  14. >>> next(rp), next(rp)
  15. (7, 7)
  16. >>> list(itertools.repeat(8, 4)) # ➑
  17. [8, 8, 8, 8]
  18. >>> list(map(operator.mul, range(11), itertools.repeat(5))) # ➒
  19. [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。

组合学生成器函数会从输入的各个元素中产出多个值

  1. >>> list(itertools.combinations('ABC', 2)) # ➊
  2. [('A', 'B'), ('A', 'C'), ('B', 'C')]
  3. >>> list(itertools.combinations_with_replacement('ABC', 2)) # ➋
  4. [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
  5. >>> list(itertools.permutations('ABC', 2)) # ➌
  6. [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
  7. >>> list(itertools.product('ABC', repeat=2)) # ➍
  8. [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'),
  9. ('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,
group) ,其中 key 是分组标准, group 是生成器,
用于产出分组里的元素

内置 reversed(seq)

从后向前,倒序产出 seq 中的元素; seq 必须是序
列,或者是实现了 __reversed__ 特殊方法的对象

itertools tee(it, n=2)

产出一个由 n 个生成器组成的元组,每个生成器
用于单独产出输入的可迭代对象中的元素

itertools.groupby 函数的用法

  1. >>> list(itertools.groupby('LLLLAAGGG')) # ➊
  2. [('L', <itertools._grouper object at 0x102227cc0>),
  3. ('A', <itertools._grouper object at 0x102227b38>),
  4. ('G', <itertools._grouper object at 0x102227b70>)]
  5. >>> for char, group in itertools.groupby('LLLLAAAGG'): # ➋
  6. ... print(char, '->', list(group))
  7. ...
  8. L -> ['L', 'L', 'L', 'L']
  9. A -> ['A', 'A',]
  10. G -> ['G', 'G', 'G']
  11. >>> animals = ['duck', 'eagle', 'rat', 'giraffe', 'bear',
  12. ... 'bat', 'dolphin', 'shark', 'lion']
  13. >>> animals.sort(key=len) # ➌
  14. >>> animals
  15. ['rat', 'bat', 'duck', 'bear', 'lion', 'eagle', 'shark',
  16. 'giraffe', 'dolphin']
  17. >>> for length, group in itertools.groupby(animals, len): # ➍
  18. ... print(length, '->', list(group))
  19. ...
  20. 3 -> ['rat', 'bat']
  21. 4 -> ['duck', 'bear', 'lion']
  22. 5 -> ['eagle', 'shark']
  23. 7 -> ['giraffe', 'dolphin']
  24. >>> for length, group in itertools.groupby(reversed(animals), len): # ➎
  25. ... print(length, '->', list(group))
  26. ...
  27. 7 -> ['dolphin', 'giraffe']
  28. 5 -> ['shark', 'eagle']
  29. 4 -> ['lion', 'bear', 'duck']
  30. 3 -> ['bat', 'rat']
  31. >>>

❶ groupby 函数产出 (key, group_generator) 这种形式的元组。
❷ 处理 groupby 函数返回的生成器要嵌套迭代:这里在外层使用 for
循环,内层使用列表推导。
❸ 为了使用 groupby 函数,要排序输入;这里按照单词的长度排序。
❹ 再次遍历 key 和 group 值对,把 key 显示出来,并把 group 扩展成
列表。
❺ 这里使用 reverse 生成器从右向左迭代 animals。

最后一个生成器函数是 iterator.tee,这个函数只有一个
作用:从输入的一个可迭代对象中产出多个生成器,每个生成器都可以产出输入的各个元素。产出的生成器可以单独使用

  1. >>> list(itertools.tee('ABC'))
  2. [<itertools._tee object at 0x10222abc8>, <itertools._tee object at 0x10222ac08>]
  3. >>> g1, g2 = itertools.tee('ABC')
  4. >>> next(g1)
  5. 'A'
  6. >>> next(g2)
  7. 'A'
  8. >>> next(g2)
  9. 'B'
  10. >>> list(g1)
  11. ['B', 'C']
  12. >>> list(g2)
  13. ['C']
  14. >>> list(zip(*itertools.tee('ABC')))
  15. [('A', 'A'), ('B', 'B'), ('C', 'C')]

Python 3.3中新出现的句法:yield from

如果生成器函数需要产出另一个生成器生成的值,传统的解决方法是使用嵌套的 for 循环。

  1. >>> def chain(*iterables):
  2. ... for it in iterables:
  3. ... for i in it:
  4. ... yield i
  5. ...
  6. >>> s = 'ABC'
  7. >>> t = tuple(range(3))
  8. >>> list(chain(s, t))
  9. ['A', 'B', 'C', 0, 1, 2]
  1. >>> def chain(*iterables):
  2. ... for i in iterables:
  3. ... yield from i
  4. ...
  5. >>> list(chain(s, t))
  6. ['A', 'B', 'C', 0, 1, 2]

可以看出,yield from i 完全代替了内层的 for 循环。在这个示例中使用 yield from 是对的,而且代码读起来更顺畅,不过感觉更像是语法糖。
除了代替循环之外,yield from 还会创建通道,把内层生成器直接与外层生成器的客户端联系起来。把生成器当成协程使用时,这个通道特别重要,不仅能为客户端代码生成值,还能使用客户端代码提供的值。

流畅的python 14章可迭代的对象、迭代器 和生成器的更多相关文章

  1. Python核心编程的四大神兽:迭代器、生成器、闭包以及装饰器

      生成器 生成器是生成一个值的特殊函数,它具有这样的特点:第一次执行该函数时,先从头按顺序执行,在碰到yield关键字时该函数会暂停执行该函数后续的代码,并且返回一个值:在下一次调用该函数执行时,程 ...

  2. 流畅的python第十四章可迭代的对象,迭代器和生成器学习记录

    在python中,所有集合都可以迭代,在python语言内部,迭代器用于支持 for循环 构建和扩展集合类型 逐行遍历文本文件 列表推导,字典推导和集合推导 元组拆包 调用函数时,使用*拆包实参 本章 ...

  3. 流畅的python第二章序列构成的数组学习记录

    python内置序列类型概览 列表推导和生成器表达式 列表推导是构建列表的快捷方式,而生成器表达式可以用来创建其他任何类型的序列 列表推导的示例 >>>test = [i*2 for ...

  4. python编程系列---可迭代对象,迭代器和生成器详解

    一.三者在代码上的特征 1.有__iter__方法的对象就是可迭代类(对象) 2.有__iter__方法,__next()方法的对象就是迭代器3.生成器 == 函数+yield 生成器属于迭代器, 迭 ...

  5. Python开发【第五篇】迭代器、生成器、递归函数、二分法

    阅读目录 一.迭代器 1. 迭代的概念 #迭代器即迭代的工具(自定义的函数),那什么是迭代呢? #迭代:指一个重复的过程,每次重复都可以称之为一次迭代,并且每一次重复的结果是下一个迭代的初始值(例如: ...

  6. Python 入门基础11 --函数基础4 迭代器、生成器、枚举类型

    今日目录: 1.迭代器 2.可迭代对象 3.迭代器对象 4.for循环迭代器 5.生成器 6.枚举对象 一.迭代器: 循环反馈的容器(集合类型) 每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的 ...

  7. Python——五分钟带你弄懂迭代器与生成器,夯实代码能力

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是周一Python专题,给大家带来的是Python当中生成器和迭代器的使用. 我当初第一次学到迭代器和生成器的时候,并没有太在意,只是觉 ...

  8. [流畅的Python]第一章数据模型

    这些来自同一家出版社的动物书 像是计算机科学界一盏盏指路明灯 余幼时 初试读 学浅 以为之晦涩难懂 像是老学究咬文嚼字 现在看起来还有些有趣 其实理工男大多都很有趣 这一章介绍了 怎么样去视线一个带有 ...

  9. Head First Python 1-4章学习感悟

    一.学习知识总结 (1)迭代         range(起始值,结束值,步长):包含起始值,不包含结束值,步长为正数 (2)导入模块 from datetime import datetime (3 ...

随机推荐

  1. Python 集合常用方法总结

    数据类型:int/str/bool/list/dict/tuple/float/set   (set类型天生去重) 一.集合的定义 s = set()  #定义空集合 s = {'a','b','c' ...

  2. myeclipce怎么破解

    MyEclipse安装文件下载,下载地址 http://www.jb51.net/softs/150886.html 你也可以进入官方网站下载:http://www.myeclipsecn.com/d ...

  3. sitemesh 2.4 装饰器学习

    SiteMesh 是一个网页布局和修饰的框架,利用它可以将网页的内容和页面结构分离,以达到页面结构共享的目的 SiteMesh是OpenSymphony团队开发的JEE框架之一,它是一个非常优秀的页面 ...

  4. phpcms 初次建站心得

    最近要给客户建个网站,考虑到效率问题,直接找了个开源的phpcms,(现在被收购了,以前的时候我还知道是个开源的).由于对这个东西不熟悉,原来就是了解一些,php的建站系统,php的MVC框架.故此, ...

  5. ES6快速入门使用

    https://www.jianshu.com/p/061304806bda Babel-webpack的配置 Bebal官方文档 前段项目我想使用ES6的语法我应该怎么做呢?我们可以查看Babel的 ...

  6. C0302 将一个代码块中的内容保存在文件中, 查看一个rpm包是否可以安装

    #!/bin/bash # 这个脚本是用来描述和确认是否可以安装一个rpm包 # 在一个文件中保存输出 SUCCESS=0 E_NOARGS=65 if [ -z "$1" ] t ...

  7. C++ 类的多态三(多态的原理--虚函数指针--子类虚函数指针初始化)

    //多态的原理--虚函数指针--子类虚函数指针初始化 #include<iostream> using namespace std; /* 多态的实现原理(有自己猜想部分) 基础知识: 类 ...

  8. C++ 类的继承五(类继承中的static关键字)

    //类继承中的static关键字 #include<iostream> using namespace std; /* 派生类中的静态成员 基类定义的静态成员,将被所有派生类共享 根据静态 ...

  9. php 怎么查看是否开启了socket

    <?php if(extension_loaded('sockets')){ echo "已开启"; }else{ echo "未开启"; } ?>

  10. MAC Ruby版本需要升级至2.2.2以上

    第一例: 默认情况下,Mac OS X 系统已经安装好 Ruby(最新的 Mavericks 随机的 Ruby 版本为 2.0.0p247),安装在 /System/Library/Framework ...