Python 中的生成器相关主题

PythonPythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

本节介绍一些与生成器相关的其他主题,包括生成器表达式和 itertools 模块。

生成器表达式

列表推导式的生成器版本。

>>> a = [1,2,3,4]
>>> b = (2*x for x in a)
>>> b
<generator object at 0x58760>
>>> for i in b:
...   print(i, end=' ')
...
2 4 6 8
>>>

与列表推导式的区别:

  • 不构建列表。
  • 唯一有用的目的是迭代。
  • 一旦被消耗,就不能再使用。

通用语法:

(<表达式> for i in s if <条件>)

它也可以用作函数参数:

sum(x*x for x in a)

它可以应用于任何可迭代对象:

>>> a = [1,2,3,4]
>>> b = (x*x for x in a)
>>> c = (-x for x in b)
>>> for i in c:
...   print(i, end=' ')
...
-1 -4 -9 -16
>>>

生成器表达式的主要用途是在对序列执行某些计算但只使用结果一次的代码中。例如,从文件中去除所有注释:

f = open('somefile.txt')
lines = (line for line in f if not line.startswith('#'))
for line in lines:
  ...
f.close()

使用生成器,代码运行得更快且占用内存少。它就像应用于流的过滤器。

为什么使用生成器

  • 许多问题用迭代来表达会清晰得多。
    • 遍历一组项目并执行某种操作(搜索、替换、修改等)。
    • 处理管道可应用于广泛的数据处理问题。
  • 更高的内存效率。
    • 仅在需要时生成值。
    • 与构建巨大的列表形成对比。
    • 可对流式数据进行操作
  • 生成器促进代码复用
    • 将迭代与使用迭代的代码分离
    • 你可以构建一个包含有趣迭代函数的工具箱并进行混合搭配。

itertools 模块

itertools 是一个库模块,包含各种用于帮助处理迭代器/生成器的函数。

itertools.chain(s1,s2)
itertools.count(n)
itertools.cycle(s)
itertools.dropwhile(predicate, s)
itertools.groupby(s)
itertools.ifilter(predicate, s)
itertools.imap(function, s1,... sN)
itertools.repeat(s, n)
itertools.tee(s, ncopies)
itertools.izip(s1,..., sN)

所有函数都以迭代方式处理数据。它们实现了各种迭代模式。

更多信息请参考 PyCon '08 的教程 Generator Tricks for Systems Programmers

在之前的练习中,你编写了一些代码来跟踪写入日志文件的行,并将它们解析成一系列行。本练习在此基础上继续进行。确保 stocksim.py 仍在运行。

练习6.13:生成器表达式

生成器表达式是列表推导式的生成器版本。例如:

>>> nums = [1, 2, 3, 4, 5]
>>> squares = (x*x for x in nums)
>>> squares
<generator object <genexpr> at 0x109207e60>
>>> for n in squares:
...     print(n)
...
1
4
9
16
25

与列表推导式不同,生成器表达式只能使用一次。因此,如果你再尝试另一个for循环,就不会得到任何结果:

>>> for n in squares:
...     print(n)
...
>>>

练习6.14:函数参数中的生成器表达式

生成器表达式有时会放在函数参数中。一开始看起来有点奇怪,但试试这个实验:

>>> nums = [1,2,3,4,5]
>>> sum([x*x for x in nums])    ## 一个列表推导式
55
>>> sum(x*x for x in nums)      ## 一个生成器表达式
55
>>>

在上面的例子中,如果处理的是一个大列表,使用生成器的第二个版本会显著减少内存使用。

在你的 portfolio.py 文件中,你进行了一些涉及列表推导式的计算。尝试用生成器表达式替换这些。

✨ 查看解决方案并练习

练习6.15:代码简化

生成器表达式常常是小型生成器函数的有用替代品。例如,不用编写这样的函数:

def filter_symbols(rows, names):
    for row in rows:
        if row['name'] in names:
            yield row

你可以编写如下代码:

rows = (row for row in rows if row['name'] in names)

修改 ticker.py 程序,酌情使用生成器表达式。

✨ 查看解决方案并练习

总结

恭喜你!你已经完成了“更多生成器”实验。你可以在LabEx中练习更多实验来提升你的技能。