使用+=&引用;在计数器数组中,Python导致了意外的行为

使用+=&引用;在计数器数组中,Python导致了意外的行为,python,operators,Python,Operators,我正在使用Python 3.4.1,我想知道以下情况: 给定一个计数器数组 cnt = [Counter()] * n 我想在一个特定的位置添加一些项目,所以我这样做了 cnt[i] += Counter(x) 对于构造“+=”,我试图 cnt[i] = cnt[i] + Counter(x) 但是,我得到的不是我所期望的,而是与之相当的东西 for i in range(0, n): cnt[i] = cnt[i] + Counter(x) 换句话说,它在数组中添加了我的所有计

我正在使用Python 3.4.1,我想知道以下情况:

给定一个计数器数组

cnt = [Counter()] * n
我想在一个特定的位置添加一些项目,所以我这样做了

cnt[i] += Counter(x)
对于构造“+=”,我试图

cnt[i] = cnt[i] + Counter(x)
但是,我得到的不是我所期望的,而是与之相当的东西

for i in range(0, n):
    cnt[i] = cnt[i] + Counter(x)
换句话说,它在数组中添加了我的所有计数器

  • 这种行为(添加数组的每个项)在Python中常见吗? 我解释错了什么吗
  • 有没有一个正确/简单/安全的方法来写我想要的
  • 这是版本的错误吗
一个简短的例子:

from collections import Counter

text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
cnt = [Counter()] * 2

i = 0
for c in text:
    cnt[i] += Counter(c)  # cnt[i] = cnt[i] + Counter(c)
    i = (i+1) % 2

for i in range(0, 2):
    print(cnt[i], i)
输出:

Counter({' ': 7, 'i': 6, 'e': 5, 't': 5, 'o': 4, 's': 4, 'm': 3, 'r': 3, 'c': 3, 'u': 2, 'p': 2, 'a': 2, 'l': 2, 'd': 2, 'n': 2, '.': 1, 'g': 1, 'L': 1, ',': 1}) 0
Counter({' ': 7, 'i': 6, 'e': 5, 't': 5, 'o': 4, 's': 4, 'm': 3, 'r': 3, 'c': 3, 'u': 2, 'p': 2, 'a': 2, 'l': 2, 'd': 2, 'n': 2, '.': 1, 'g': 1, 'L': 1, ',': 1}) 1
预期产出:

Counter({'t': 4, 'i': 3, 'r': 3, 's': 2, 'e': 2, 'm': 2, 'c': 2, 'n': 2, 'a': 2, 'l': 2, ',': 1, 'd': 1, ' ': 1, 'L': 1}) 0
Counter({' ': 6, 'o': 4, 'i': 3, 'e': 3, 's': 2, 'u': 2, 'p': 2, '.': 1, 't': 1, 'g': 1, 'd': 1, 'm': 1, 'c': 1}) 1

当您执行
cnt=[Counter()]*n
时,您要做的是创建一个计数器,然后使列表中的每个元素都指向该计数器。您不是在创建
n
计数器,而是在创建单个计数器

这是因为在Python中,所有内容都是通过引用存储的(类似于.More-info)。实际上,您复制了对计数器对象的引用
n次
,而不是计数器本身

这意味着执行
cnt[i]+=Counter(x)
将修改底层计数器,使其看起来像整个列表已更改一样

要解决此问题,请尝试执行以下操作:

cnt = [Counter() for i in range(n)]

现在,您正在真正创建
n
不同的计数器(因为您调用构造函数
n
次),并将获得预期的行为。

cnt数组的所有元素都引用同一个计数器。使用
*
运算符时,您没有复制它。您确定吗?我已经在其他时候使用过
*
,效果很好,也就是说,可以制作不同的副本。@MFS:不,没有。如果您使用的是不可变对象,您可能会认为是这样,但即使如此,您对一个对象有多个引用。不可变只是以不同的方式处理像
+=
这样的事情。特别是,如果你有一个数字数组,它会工作,因为它们是不可变的。因此它会更新数组引用,而不是共享对象。@user3885927:这是因为在更高版本的Python中,计数器中添加了对
\uuu\iadd\uu
的支持。我不记得是哪一个,但3.4应该有。好的,我明白了。但是,您知道为什么当我更改注释行(在我给出的示例中)时,我达到了预期的输出吗?@MFS:
a+=b
并不等同于
a=a+b
+=
请求对象通过修改其状态来执行操作,而不是通过返回新对象(如果它支持修改的话)。