当我写:
lines = (line.strip() for line in open('a_file'))
文件是立即打开还是仅在我开始使用生成器表达式时才访问文件系统?
当我写:
lines = (line.strip() for line in open('a_file'))
文件是立即打开还是仅在我开始使用生成器表达式时才访问文件系统?
它立即打开。如果你使用一个不存在的文件名,你可以验证这一点(它会抛出一个异常,表明 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.
open()
在生成器构建时立即调用,无论您何时或是否从中消耗。
相关规范是PEP-289:
早期绑定与后期绑定
经过多次讨论,决定应立即计算第一个(最外层)for 表达式,并在执行生成器时计算剩余的表达式。
当被要求总结绑定第一个表达式的原因时,Guido 提供了 [5]:
考虑
sum(x for x in foo())
。现在假设有一个错误foo()
引发了异常,并且一个错误在sum()
开始迭代其参数之前引发了异常。您希望看到哪个例外?sum()
如果引发了 in 而不是 in ,我会感到惊讶foo()
,因为调用 tofoo()
是 to 参数的一部分sum()
,并且我希望在调用函数之前处理参数。OTOH, in
sum(bar(x) for x in foo())
, wheresum()
和foo()
没有错误,但bar()
引发异常,我们别无选择,只能延迟调用 tobar()
直到sum()
开始迭代——这是生成器合同的一部分。next()
(在他们的方法第一次被调用之前,他们什么都不做。)
有关进一步讨论,请参阅该部分的其余部分。
它立即打开。
例子:
def func():
print('x')
return [1, 2, 3]
g = (x for x in func())
输出:
x
该函数需要返回一个可迭代对象。
open()
返回一个可迭代的打开文件对象。因此,当您定义生成器表达式时,该文件将被打开。