为什么 a = a['k'] = {} 会创建一个无限嵌套的字典?

Why does a = a['k'] = {} create an infinitely nested dictionary?

提问人:Susam Pal 提问时间:2/21/2019 最后编辑:Susam Pal 更新时间:11/13/2021 访问量:2523

问:

这是我的 Python 代码,它创建了一个无限嵌套的字典:

a = a['k'] = {}

print(a)
print(a['k'])
print(a['k']['k'])
print(a is a['k'])

输出如下:

{'k': {...}}
{'k': {...}}
{'k': {...}}
True

输出显示 that 引用自身,使其无限嵌套。a['k']a

我猜这句话:

a = a['k'] = {}

行为如下:

new = {}
a = new
a['k'] = new

这确实会创建一个无限嵌套的字典。

我查看了 Python 语言参考第 7.2 节:赋值语句,但我找不到任何暗示应该首先设置为新字典,然后在该字典中插入键/值对的内容。以下是我认为相关但没有回答我的问题的参考资料的一些摘录:a = a['k'] = {}a

如果目标列表是没有尾随逗号的单个目标(可选)在括号中,则将对象分配给该目标。

如果目标是订阅:将计算引用中的主要表达式。它应该生成一个可变序列对象(如列表)或一个映射对象(如字典)。接下来,计算下标表达式。

如果主对象是映射对象(如字典),则下标必须具有与映射的键类型兼容的类型,然后要求映射创建将下标映射到指定对象的键/基准对。这可以将现有的键/值对替换为相同的键值,也可以插入新的键/值对(如果不存在具有相同值的键)。

这些摘录中的每一个都定义了具有单个目标(例如 和 )的作业的行为,但它们似乎没有讨论在 .这种陈述的评估顺序记录在哪里?a = {}a['k'] = {}a = a['k'] = {}


这个问题现在已经解决了。GPhilo的回答指出了第7.2节的相关条款:转让语句。相关条款一开始就是对的,但我之前忽略了它。在这里:

赋值语句计算表达式列表(请记住,它可以是单个表达式或逗号分隔的列表,后者生成一个元组),并将单个生成的对象从左到右分配给每个目标列表。

现在让我们将其与语法进行比较。

赋值语句定义为

assignment_stmt ::=  (target_list "=")+ (starred_expression | yield_expression)

所以声明

a = a['k'] = {}

有两个元素,即 和 ,以及一个元素,即 ,因此从左到右分配给每个目标列表。target_listaa['k']starred_expression{}{}aa['k']

python 字典 参考 值运算符 优先级

评论


答:

10赞 GPhilo 2/21/2019 #1

根据您引用的第 7.2 节(强调我的),分配语句中的分配是从左到右解决的:

赋值语句计算表达式列表(请记住 这可以是单个表达式,也可以是逗号分隔的列表,后者 生成一个元组),并将单个结果对象分配给每个 目标列表,从左到右

这意味着是的,实际上您的陈述等同于:

new = {}
a = new
a['k'] = new

作为快速的反证,交换分配的顺序会导致错误:

a['k'] = a = {}

提高

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

评论

2赞 Susam Pal 2/21/2019
谢谢。这是有道理的。赋值语句定义为 ,因此有两个元素 ( 和 ) 和一个元素 (),因此从左到右分配给每个目标列表。assignment_stmt ::= (target_list "=")+ (starred_expression | yield_expression)a = a['k'] = {}target_listaa['k']starred_expression{}{}aa['k']
0赞 Adarsh TS 8/19/2020
我还是不明白。如果结果对象到每个目标列表,从左到右都是这种情况,那么 a 应该等于 并且应该返回 ,对吧?我无法理解为什么这里会发生无限嵌套。{'k': {}}a['k']{}
0赞 GPhilo 8/19/2020
不,首先计算最右侧的表达式(即赋值的右侧),因此定义。然后,从左到右逐个计算作业左侧的表达式列表。每次评估后,在进行下一次评估之前,都会进行分配。因此,计算的第一个 ting 是 ,它只是一个名称,因此会立即分配它(因此,此时 )。然后,计算第二个表达式:is now valid,因此赋值可以通过 ()。然而,分配的对象......{}1a == {}a['k']a['k']=={}
0赞 GPhilo 8/19/2020
...是一样的,所以.a is a['k']