【问题标题】:Why does [defaultdict(int)] * 3 return three references to the same object为什么 [defaultdict(int)] * 3 返回对同一个对象的三个引用
【发布时间】:2021-12-02 03:46:47
【问题描述】:

编辑:

为什么[defaultdict(int)] * 3 会返回对同一个对象的三个引用?

原标题

将 defaultdicts 列表解包到变量中在 Python 中有意外行为

defaultdict 类型的初始化列表解压缩到变量中似乎并不像我期望的那样工作。有谁知道为什么会这样(参见下面的代码 sn-ps)?我正在使用Python 3.9.1

# Equivalent behavior - works OK
a,b,c = [int(), int(), int()]
d,e,f = [int()] * 3

# Expected equivalent behavior - BEHAVES DIFFERENTLY
l,m,p = [defaultdict(int), defaultdict(int), defaultdict(int)]
q,r,s = [defaultdict(int)] * 3

完整的 sn-p:

>>> a,b,c = [int(), int(), int()]
>>> a+=4; b+=2; c+=7
>>> a,b,c
(4, 2, 7)
>>> d,e,f = [int()] * 3
>>> d+=11; e+=8; f+= 41
>>> d,e,f
(11, 8, 41)

>>> from collections import defaultdict
>>> l,m,p = [defaultdict(int), defaultdict(int), defaultdict(int)]
>>> l['a']+=1; m['b']+=2; m['c']+=3;
>>> l,m,p
(
  defaultdict(<class 'int'>, {'a': 1}), 
  defaultdict(<class 'int'>, {'b': 2, 'c': 3}), 
  defaultdict(<class 'int'>, {})
)
>>> q,r,s = [defaultdict(int)] * 3
>>> q['a']+=111; r['b']+=222; m['c']+=333;
>>> q,r,s
(
  defaultdict(<class 'int'>, {'a': 111, 'b': 222}), 
  defaultdict(<class 'int'>, {'a': 111, 'b': 222}), 
  defaultdict(<class 'int'>, {'a': 111, 'b': 222})
)

这个问题是基于问题"Unpack list to variables"提出的主题。

【问题讨论】:

  • 您到底在说什么意外行为? [defaultdict(int)] * 3 会生成一个列表,其中包含三个对同一个 defaultdict 的引用?
  • 谢谢,这正是我的问题。我更新了标题以反映这一点。
  • 这基本上是完全相同的问题as described here。在这种情况下的修复类似于q,r,s = [defaultdict(int) for _ in range(3)]
  • 第一个代码 sn-p 上的 cmets 错误:[int()] * 3 的行为与 [defaultdict(int)] * 3 完全相同!在这两种情况下,相同的引用被复制了 3 次。自己看看:[id(x) for x in [d, e, f]].
  • @LiamFiddler 是的,我没有质疑这一点。我要说的是,声明的行为对于 int 和 defaultdicts 是相同的,这与第一个代码 sn-p 中的 OP 的 cmets 相反。因为……它是。

标签: python python-3.x initialization defaultdict unpack


【解决方案1】:

问题在于内存中的位置。一个简单的控制台测试表明:

> from collections import defaultdict
> l,m,p = [defaultdict(int), defaultdict(int), defaultdict(int)]
> id(l) == id(p)
False
> id(m) == id(p)
False

现在让我们尝试另一种方式:

> l,m,p = [defaultdict(int)] * 3
> id(l) == id(p)
True
> id(m) == id(p)
True

在第一种情况下,您要在内存中创建三个单独的插槽。第二,您在内存中创建一个点,然后在内存中创建两个指向该插槽的附加指针;因此,当您更新一个时,它们都会发生变化,因为它们都指向内存中的同一个插槽。

This answer 更详细地说明了为什么某些数据类型会发生这种情况,而其他数据类型则不会。 TL;DR - 为了优化,小整数可以在同一个对象中,但使用不同的指针。这就是为什么您可以对整数变量运行 id()is 检查并查看它们是否指向同一个对象,但在修改每个变量时让它们独立运行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-03
    • 2014-02-13
    • 2019-05-22
    • 2017-11-11
    • 2020-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多