Python 生成器表达式与列表和字典理解的可变性:嵌套字典怪异
Mutability of Python Generator Expressions versus List and Dictionary Comprehension: Nested Dictionary Weirdness
我正在使用 Python 3.5 创建一组生成器来解析一组打开的文件,以便从这些文件中挑选数据来构建我计划稍后导出的对象。我最初是在进行任何分析之前解析每个文件的整体并创建一个字典对象列表,但这个过程有时会花费 30 秒,而且由于我只需要处理每个文件的每一行一次,我认为这是使用发电机的好机会。但是,我觉得我在概念上缺少一些关于生成器的东西,也许是生成器中对象的可变性。
我制作字典列表的原始代码如下:
parsers = {}
# iterate over files in the file_name file to get their attributes
for dataset, data_file in files.items():
# Store each dataset as a list of dictionaries with keys that
# correspond to the attributes of that dataset
parsers[dataset] = [{attributes[dataset][i]: value.strip('~')
for i, value in enumerate(line.strip().split('^'))}
for line
in data_file]
我通过调用访问列表:
>>>parsers['definitions']
它按预期工作,返回字典列表。然而,当我将这个列表转换成一个生成器时,各种奇怪的事情发生了。
parsers = {}
# iterate over files in the file_name file to get their attributes
for dataset, data_file in files.items():
# Store each dataset as a list of dictionaries with keys that
# correspond to the attributes of that dataset
parsers[dataset] = ({attributes[dataset][i]: value.strip('~')
for i, value in enumerate(line.strip().split('^'))}
for line
in data_file)
我使用以下方式调用它:
>>> next(parsers['definitions'])
运行 此代码 returns 索引超出范围错误。
我可以看到两个代码段之间的主要区别是,在列表推导版本中,python 从文件构建列表并继续前进,而无需存储推导变量供以后使用。
相反,在生成器表达式中,生成器中定义的变量需要与生成器一起存储,因为它们会影响稍后在我的代码中对生成器的每次连续调用。我在想,也许生成器中的变量正在与我的代码创建的其他生成器共享一个命名空间,因此每个生成器都有不稳定的行为,基于最后一个 运行 的生成器表达式,因此设置变量的值最后。
对于此问题的原因,我很感激!
我认为问题出在您构建词典时。
attributes[dataset][i]
请注意,对于列表版本,dataset
是 dataset
在 for
循环的特定回合中的任何内容。但是,对于生成器,直到 after for 循环完成后才对该表达式求值,因此 dataset
将具有 last[= 的值30=] 来自 files.items()
循环的数据集...
这是一个超级简单的演示,希望能详细说明问题:
results = []
for a in [1, 2, 3]:
results.append(a for _ in range(3))
for r in results:
print(list(r))
请注意,我们总是得到 [3, 3, 3]
,因为当我们从生成器中取值时,a
的值是 3
。
我正在使用 Python 3.5 创建一组生成器来解析一组打开的文件,以便从这些文件中挑选数据来构建我计划稍后导出的对象。我最初是在进行任何分析之前解析每个文件的整体并创建一个字典对象列表,但这个过程有时会花费 30 秒,而且由于我只需要处理每个文件的每一行一次,我认为这是使用发电机的好机会。但是,我觉得我在概念上缺少一些关于生成器的东西,也许是生成器中对象的可变性。
我制作字典列表的原始代码如下:
parsers = {}
# iterate over files in the file_name file to get their attributes
for dataset, data_file in files.items():
# Store each dataset as a list of dictionaries with keys that
# correspond to the attributes of that dataset
parsers[dataset] = [{attributes[dataset][i]: value.strip('~')
for i, value in enumerate(line.strip().split('^'))}
for line
in data_file]
我通过调用访问列表:
>>>parsers['definitions']
它按预期工作,返回字典列表。然而,当我将这个列表转换成一个生成器时,各种奇怪的事情发生了。
parsers = {}
# iterate over files in the file_name file to get their attributes
for dataset, data_file in files.items():
# Store each dataset as a list of dictionaries with keys that
# correspond to the attributes of that dataset
parsers[dataset] = ({attributes[dataset][i]: value.strip('~')
for i, value in enumerate(line.strip().split('^'))}
for line
in data_file)
我使用以下方式调用它:
>>> next(parsers['definitions'])
运行 此代码 returns 索引超出范围错误。
我可以看到两个代码段之间的主要区别是,在列表推导版本中,python 从文件构建列表并继续前进,而无需存储推导变量供以后使用。
相反,在生成器表达式中,生成器中定义的变量需要与生成器一起存储,因为它们会影响稍后在我的代码中对生成器的每次连续调用。我在想,也许生成器中的变量正在与我的代码创建的其他生成器共享一个命名空间,因此每个生成器都有不稳定的行为,基于最后一个 运行 的生成器表达式,因此设置变量的值最后。
对于此问题的原因,我很感激!
我认为问题出在您构建词典时。
attributes[dataset][i]
请注意,对于列表版本,dataset
是 dataset
在 for
循环的特定回合中的任何内容。但是,对于生成器,直到 after for 循环完成后才对该表达式求值,因此 dataset
将具有 last[= 的值30=] 来自 files.items()
循环的数据集...
这是一个超级简单的演示,希望能详细说明问题:
results = []
for a in [1, 2, 3]:
results.append(a for _ in range(3))
for r in results:
print(list(r))
请注意,我们总是得到 [3, 3, 3]
,因为当我们从生成器中取值时,a
的值是 3
。