【问题标题】:Why does a = a['k'] = {} create an infinitely nested dictionary?为什么 a = a['k'] = {} 会创建一个无限嵌套的字典?
【发布时间】:2019-07-15 10:43:21
【问题描述】:

这是我创建无限嵌套字典的 Python 代码:

a = a['k'] = {}

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

这是输出:

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

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

我猜是这样的说法:

a = a['k'] = {}

行为如下:

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

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

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

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

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

如果主要是映射对象(例如字典),则下标必须具有与映射的键类型兼容的类型,然后要求映射创建一个键/数据对,将下标映射到分配的目的。这可以用相同的键值替换现有的键/值对,或者插入一个新的键/值对(如果不存在具有相同值的键)。

这些摘录中的每一个都定义了具有单个目标的赋值行为,例如 a = {}a['k'] = {},但它们似乎没有讨论在 a = a['k'] = {} 的情况下应该发生什么。此类声明的评估顺序记录在哪里?


这个问题现在已经解决了。 GPhilo的回答指向了Section 7.2: Assignment statements的相关条款。相关条款一开始是对的,但我之前忽略了它。这里是:

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

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

赋值语句定义为

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

所以声明

a = a['k'] = {}

有两个target_list元素,即aa['k'],还有一个starred_expression元素,即{},所以{}被分配给每个目标列表a和@ 987654344@从左到右。

【问题讨论】:

    标签: python dictionary reference assignment-operator operator-precedence


    【解决方案1】:

    赋值语句中的赋值是从左到右解析的,按照the section 7.2 you quoted(强调我的):

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

    这意味着是的,确实你的陈述相当于:

    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
    

    【讨论】:

    • 谢谢。这是有道理的。赋值语句定义为assignment_stmt ::= (target_list "=")+ (starred_expression | yield_expression),所以a = a['k'] = {}有两个target_list元素(aa['k'])和一个starred_expression元素({}),所以{}被分配给每个目标从左到右列出aa['k']
    • 我还是不明白。如果每个目标列表的结果对象,从左到右 是这种情况,那么a 应该等于{'k': {}} 并且a['k'] 应该返回{},对吗?我无法理解为什么会在这里发生无限嵌套。
    • 不,首先计算最右边的表达式(即赋值的右边),所以定义了{}。然后,赋值语句left 侧的表达式列表从左到右逐一求值。每次评估后,都会进行分配,然后再进行下一次评估。因此,评估的第一个 ting 是 1,它只是一个名称,所以它被立即分配(因此,此时,a == {})。然后,计算第二个表达式:a['k'] 现在有效,因此赋值可以通过 (a['k']=={})。但是,分配的对象...
    • ... 是一样的,所以a is a['k'].
    猜你喜欢
    • 2021-05-14
    • 2013-10-28
    • 1970-01-01
    • 2019-04-23
    • 2021-10-03
    • 2018-02-22
    • 1970-01-01
    • 2021-06-02
    • 1970-01-01
    相关资源
    最近更新 更多