【问题标题】:Generators and files生成器和文件
【发布时间】:2018-01-27 05:53:00
【问题描述】:

当我写作时:

lines = (line.strip() for line in open('a_file'))

文件是立即打开还是只有在我开始使用生成器表达式时才访问文件系统?

【问题讨论】:

  • 如果你先做open = print,那么你的代码会打印a_file
  • @StefanPochmann 我花了一些时间,但至少我理解了你的评论......非常感谢
  • @MSeifert 非常好的编辑!

标签: python generator operator-precedence generator-expression


【解决方案1】:

它立即打开。如果你使用一个不存在的文件名,你可以验证这一点(它会抛出一个异常,表明 Python 实际上试图立即打开它)。

您还可以使用提供更多反馈的函数来查看命令是否在生成器迭代之前执行:

def somefunction(filename):
    print(filename)
    return open(filename)

lines = (line.strip() for line in somefunction('a_file'))  # prints

但是,如果您使用生成器函数而不是生成器表达式,则文件仅在您对其进行迭代时才会打开:

def somefunction(filename):
    print(filename)
    for line in open(filename):
        yield line.strip()

lines = somefunction('a_file')  # no print!

list(lines)                     # prints because list iterates over the generator function.

【讨论】:

  • 感谢您对生成器表达式与生成器函数之间差异的观察。我没有批准你的答案,因为我更喜欢 the one 参考原始决策过程,但你的答案非常好!
【解决方案2】:

open() 在生成器构建时立即被调用,无论您何时或是否从中消耗。

相关规范为PEP-289:

早期绑定与晚期绑定

经过多次讨论, 决定应该评估第一个(最外层的)for 表达式 立即计算剩余的表达式 生成器被执行。

要求总结绑定第一个表达式的原因, Guido 提供 [5]:

考虑sum(x for x in foo())。现在假设foo() 中有一个错误 引发异常,以及 sum() 中引发异常的错误 在它开始迭代它的论点之前。哪个例外会 你希望看到吗?如果sum() 中的那个被提出来,我会感到惊讶 而是foo() 中的那个,因为对foo() 的调用是 sum() 的参数,我希望参数在 函数被调用。

OTOH,在sum(bar(x) for x in foo()),其中sum()foo() 是 没有错误,但是bar() 引发异常,我们别无选择,只能延迟 对bar() 的调用直到sum() 开始迭代——这是 发电机合同。 (他们什么都不做,直到他们的next() 方法是 第一次调用。)

有关进一步讨论,请参阅该部分的其余部分。

【讨论】:

    【解决方案3】:

    立即打开。

    例子:

    def func():
        print('x')
        return [1, 2, 3]
    
    g = (x for x in func())
    

    输出:

    x
    

    函数需要返回一个可迭代的对象。 open() 返回一个可迭代的打开文件对象。 因此,当您定义生成器表达式时,该文件将被打开。

    【讨论】:

      猜你喜欢
      • 2020-10-07
      • 1970-01-01
      • 1970-01-01
      • 2011-08-01
      • 1970-01-01
      • 2018-12-23
      • 1970-01-01
      • 2015-12-29
      • 2018-02-08
      相关资源
      最近更新 更多