阅读翻译Fluent Python之An Arithmetic Progression Generator(第17章的一个小节)

阅读翻译Fluent Python之An Arithmetic Progression Generator(第17章的一个小节)

关于

  • 首次发表日期: 2024-07-16
  • Fluent Python亚马逊链接: https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1492056359
  • 使用ChatGPT和KIMI机翻,然后人工润色
  • 如有错误,请不吝指出

An Arithmetic Progression Generator(算术级数生成器)

经典的迭代器模式主要用于遍历:浏览某种数据结构。但当项目是即时生成生成时, 基于一个方法(method)去读取系列(series)中下一个项目的标准接口也是非常有用的。例如,内置的 range 函数生成一个有界的算术级数(AP)整数。如果你需要生成任何类型的数字的算术级数,而不仅仅是整数,怎么办?

示例 17-11 展示了一些对 ArithmeticProgression 类的控制台测试,我们稍后会看到该类的实现。示例 17-11 中构造函数的签名是 ArithmeticProgression(begin, step[, end])。内置 range 函数的完整签名是 range(start, stop[, step])。我选择实现一个不同的签名,因为在算术级数中,步长是必需的,而终点是可选的。我还将参数名称从 start/stop 改为 begin/end,以明确我选择了不同的签名。在示例 17-11 中的每个测试中,我都对结果调用 list() 以检查生成的值。

在这里插入图片描述

注意,根据 Python 算术的数值强制转换规则,生成的算术级数中数字的类型与 begin + step 的类型一致。在示例 17-11 中,你可以看到 intfloatFractionDecimal 类型的列表。示例 17-12 列出了 ArithmeticProgression 类的实现。

在这里插入图片描述

  1. __init__需要两个参数:beginstepend是可选的,如果它是None,序列将是无界的。
  2. 获取self.beginself.step相加的类型。例如,如果一个是int,另一个是floatresult_type将是float
  3. 这行代码创建了一个结果,其数值与self.begin相同,但被强制转换为后续加法的类型。
  4. 为了可读性,如果self.end属性是Noneforever标志将为True,从而产生一个无界序列。
  5. 这个循环会一直运行,直到结果匹配或超过self.end。当这个循环退出时,函数也会退出。
  6. 当前result被产生。
  7. 计算下一个可能的结果。它可能永远不会被产出,因为while循环可能会终止。

在示例17-12的最后一行中,我没有选择每次循环时将self.step加到前一个结果上,而是选择忽略前一个结果,并通过将self.begin加上self.step乘以index来得到每一个新结果。这样可以避免连续加法后浮点错误累积的影响。下面这些简单的实验清楚地展示了差异:

在这里插入图片描述

示例17-12中的ArithmeticProgression类按预期工作,并且是使用生成器函数实现__iter__特殊方法的另一个例子。然而,如果一个类的全部目的就是通过实现__iter__来构建一个生成器,我们可以将这个类替换为一个生成器函数。毕竟,生成器函数就是一个生成器工厂。

示例 17-13 展示了一个名为 aritprog_gen 的生成器函数,它与 ArithmeticProgression 做相同的工作,但代码更少。如果你调用 aritprog_gen 而不是 ArithmeticProgression,示例 17-11 中的所有测试都能通过。

Example 17-13. The aritprog_gen generator function

def aritprog_gen(begin, step, end=None):
    result = type(begin + step)(begin)
    forever = end is None
    index = 0
    while forever or result < end:
        yield result
        index += 1
        result = begin + step * index

示例 17-13 很优雅,但请记住:标准库中有很多现成的生成器可供使用,下一节将展示使用 itertools 模块的更简短的实现。

Arithmetic Progression with itertools (使用 itertools 实现算术级数)

Python 3.10中的itertools模块有20个生成器函数,它们可以以多种有趣的方式组合使用。

例如,itertools.count函数返回一个生成器,它产生数字。如果没有参数,它会产生从0开始的一系列整数。但你可以提供可选的起始和步长值,以实现与我们的aritprog_gen函数类似的结果:

在这里插入图片描述

itertools.count 永远不会停止,因此如果你调用 list(count()),Python 将尝试构建一个列表,它会填满所有曾经制造的内存芯片。际上,你的机器在调用失败之前很久就会变得非常不高兴。

另一方面,还有 itertools.takewhile 函数:它返回一个生成器,消耗另一个生成器,并在给定的判定(predicate)评估为 False 时停止。因此,我们可以结合这两个函数,编写如下代码:

>>> gen = itertools.takewhile(lambda n: n < 3, itertools.count(1, .5))
>>> list(gen)
[1, 1.5, 2.0, 2.5]

利用 takewhilecount,示例 17-14 更加简洁。

示例 17-14. aritprog_v3.py:像之前的 aritprog_gen 函数一样工作

import itertools

def aritprog_gen(begin, step, end=None):
    first = type(begin + step)(begin)
    ap_gen = itertools.count(first, step)
    if end is None:
        return ap_gen
    return itertools.takewhile(lambda n: n < end, ap_gen)

请注意,在示例17-14中的aritprog_gen不是一个生成器函数:它的主体中没有yield。但它返回一个生成器,就像生成器函数所做的那样。

然而,回想一下itertools.count是重复添加步长的,所以它产生的浮点数序列不如示例17-13那样精确。

示例17-14的要点是:在实现生成器时,要了解标准库中已有的内容,否则你很有可能会重新发明轮子。这就是为什么下一节将介绍几个现成的生成器函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值