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

0. 前言

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

1. 解析式

  • 解析式类型:
    • 列表解析式:[]
    • 集合解析式:{}
    • 字典解析式:{}
  • 解析式都是语法糖,可以条件规律元素。
  • 举例:
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程序如下:
repeater = Repeater('Hello')
for item in repeater:
    print(item)
  • 上面的程序等价于下面的程序
repeater = Repeater('Hello')
iterator = repeater.__init__()
while True:
    item = iterator.__next__()
    print(item)

2.5. 迭代器实例

  • 典型迭代器(《流畅的Python》示例14-4)
    • 迭代器和可迭代对象分开。
    • 这样比较清晰,符合设计模式,但很麻烦。
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
  • 迭代器和可迭代对象是同一个类。
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

3. 生成器

  • 生成器可以理解为简化版的迭代器

3.1. 基本生成器

  • 两种形式:
    • 类形式的生成器:定义 __iter__ 方法,并在该方法中使用 yield 关键字。
      • 可以实现带有额外状态的生成器。
    • 函数形式的生成器:使用yield替代return
      • 调用生成器函数就会返回一个生成器对象。
      • 生成器函数就是生成器工厂。
  • 生成器函数的工作原理(以函数形式为例):
    • 当第一次调用生成器时,从函数开头开始运行,到第一个yield结束。
    • 当第二次(或多次)调用生成器时,从上一次结束的 yield 语句开始运行程序。
  • 特点:惰性实现。尽可能延后生成值,从而节约内存,避免无用处理。

3.2. 生成器表达式

  • 与列表表达式的定义方式非常类似,区别在于列表表达式使用[],而生成器表达式使用 ()
  • 可理解为列表推导的惰性版本:不会迫切构建列表,而是返回一个生成器,按需惰性生成元素。
  • 生成器表达式是语法糖,完全可以替换生成器函数。
  • 生成器表达式与普通生成器的选择:
    • 如果生成器表达式要分成多行,则尽量用生成器函数,以便提高可读性。
  • yield from:生成器函数需要产生另一个生成器生成的值,可以通过嵌套for循环,也可以使用 yield from
  • 举例:
# 生成器表达式
iterator = ('Hello' for i in range(3))

# 上述表达式等价于下面代码
def bounded_repeater(value, max_repeats):
    for i in range(max_repeats):
        yield value

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() 合并多个有序序列。

3.4. 生成器实例

  • 类形式的生成器
class Test:
    def __iter__(self):
        for i in range(10):
            yield i

  • 函数形式的生成器
def gen_123():
    print('start')
    yield 1
    print('continue')
    yield 2
    print('final')
    yield 3

4. 迭代器链

  • 基本概念:
    • 链接多个迭代器,或者说,多个迭代器嵌套使用。
    • 另一种理解,将生成器产生的数据看作,迭代器链就是将这个数据流在多个迭代器形成的管道中流过。
  • 特点:
    • 每次只处理一个元素。
    • 处理步骤之间没有缓冲区。
  • 实例(来自《深入理解Python特性》6.7节)
def integers():
    for i in range(1, 9):
        yield i

def squared(seq):
    for i in seq:
        yield i * i

def negated(seq):
    for i in seq:
        yield -i
        
chain = negated(squared(integers()))
print(list(chain)) # [-1, -4, -9, -16, -25, -36, -49, -64, -81]
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值