Python手册(5) 解析式、生成器、迭代器

0. 前言

  • 参考资料:
    • 《Python Cookbook》第四章
    • 《流畅的Python》第十四章
    • 《深入理解Python特性》第六章
  • 迭代器和生成器属于同一概念,生成器可看作是简化版的迭代器。

1. 解析式

  • 解析式类型:
    • 列表解析式:[]
    • 集合解析式:{}
    • 字典解析式:{}
  • 解析式都是语法糖,可以条件规律元素。
  • 举例:
    1
    2
    3
    squares = [x*x for x in range(10) if x % 2 == 0]
    squares_set = {x*x for x in range(10)}
    squares_dict = {x: x*x for x in range(5)}

2. 迭代器

2.1. 迭代器模式

  • 迭代器模式可用来:
    • 访问一个聚合对象的内容而无需暴露它的内部表示。
    • 支持对聚合对象的多种遍历。
    • 为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代)。

2.2. 可迭代对象与迭代器

  • 可迭代对象:
    • 使用 iter 函数可以获取迭代器的对象。
    • 抽象基类:collections.abc.Iterable
  • 迭代器:
    • 实现了无参数的 __next__ 方法,返回序列中的下一个元素。如果没有元素,则抛出 StopIteration 异常。
    • 抽象基类:collections.abc.Iterator
  • 不建议可迭代对象和迭代器实现在一个类中:
    • 这是常见的反模式。

2.3. iter函数

  • 解释器需要迭代对象时,会自动调用 iter 函数。
    • iter 函数的另外一种用法:iter(a, flag),第一个参数是可调用对象,第二个参数是哨兵符,当可调用对象返回这个哨兵符时,抛出异常,不返回值。
  • 作用:
    • 检查是否实现 __iter__ 方法,如果实现了就调用,获取迭代器。
    • 如果没有实现 __iter__ 方法,但实现了 __getitem__ 方法,就会自动创建一个迭代器,尝试按顺序(从0开始)获取元素。
    • 如果尝试失败,则抛出 TypeError 并提示 XX Object is not iterable
  • 如果类实现了 __iter__ 方法,则 issubclass(Foo, abc.Iterable) 取值为 True

2.4. for-in 循环在 Python 中的工作原理

  • 如果Python程序如下:

    1
    2
    3
    repeater = Repeater('Hello')
    for item in repeater:
    print(item)
  • 上面的程序等价于下面的程序

    1
    2
    3
    4
    5
    repeater = Repeater('Hello')
    iterator = repeater.__init__()
    while True:
    item = iterator.__next__()
    print(item

3. 生成器

3.1. 基本生成器

  • __iter__ 函数中使用 yield 关键字。
  • 只要在Python函数定义体中有 yield 关键字,该函数就是生成器函数。
  • 调用生成器函数就会返回一个生成器对象。
  • 生成器函数就是生成器工厂。
  • 生成器函数的工作原理:
    • 当第一次调用生成器时,从函数开头开始运行,到第一个yield结束。
    • 当第二次(或多次)调用生成器时,从上一次结束的 yield 语句开始运行程序。
  • 惰性实现:尽可能延后生成值,从而节约内存,避免无用处理。

3.2. 生成器表达式

  • 可裂解为列表推导的惰性版本:不会迫切构建列表,而是返回一个生成器,按需惰性生成元素。
  • 生成器表达式是语法糖,完全可以替换生成器函数。
  • 生成器表达式与普通生成器的选择:
    • 如果生成器表达式要分成多行,则尽量用生成器函数,以便提高可读性。
  • yield from:生成器函数需要产生另一个生成器生成的值,可以通过嵌套for循环,也可以使用 yield from

3.3. 标准库中的生成函数

  • itertools
    • compress
    • dropwhile
    • filterfalse
    • islice
    • takewhile
    • accumulate
    • startmap
  • 内置:
    • filter
    • enumerate
    • map
  • 举例:
    • 通过 itertools.islice() 函数可以对迭代器做切片操作。
    • 通过 itertools.dropwhile() 来跳过可迭代对象中的前一部分元素。
    • 通过 itertools.permutations()来获取所有可能的组合或排列。
    • 通过 enumerate 获取索引+值的迭代序列。
    • 通过 zip 同时迭代多个序列。
    • 通过 itertools.chain 在不同容器中进行迭代。
    • 通过 heapq.merge() 合并多个有序序列。

4. Examples

4.1. 解析器与生成器表达式

1
2
3
4
squares = [x*x for x in range(10) if x % 2 == 0]
squares_set = {x*x for x in range(10)}
squares_dict = {x: x*x for x in range(5)}
squares_iter = (x*x for x in range(10)

4.2. 迭代器

  • 典型迭代器(《流畅的Python》示例14-4)

    • 迭代器和可迭代对象分开。
    • 这样比较清晰,符合设计模式,但很麻烦。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      import re
      import reprlib

      RE_WORD = re.compile('\w+')

      class Sentence:
      def __init__(self, text):
      self.text = text
      self.words = RE_WORD.findall(test)

      def __repr__(self):
      return 'Sentence(%s)' % reprlib.repr(self.text)

      def __iter__(self):
      return SentenceIterator(self.words)

      class SentenceIterator:
      def __init__(self, words):
      self.words = words
      self.index = 0

      def __next__(self):
      try:
      word = self.words[self.index]
      except IndexError:
      raise StopIteration()
      self.index += 1
      return word

      def __iter__(self):
      return self
  • 迭代器和可迭代对象是同一个类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import re
    import reprlib

    RE_WORD = re.compile('\w+')

    class Sentence:
    def __init__(self, text):
    self.text = text
    self.words = RE_WORD.findall(test)
    self.index = 0

    def __repr__(self):
    return 'Sentence(%s)' % reprlib.repr(self.text)

    def __next__(self):
    try:
    word = self.words[self.index]
    except IndexError:
    raise StopIteration()
    self.index += 1
    return word

    def __iter__(self):
    return self

4.3. 生成器

1
2
3
4
5
6
7
def gen_123():
print('start')
yield 1
print('continue')
yield 2
print('final')
yield 3