为什么dict.get(key)起作用但dict [key]不起作用?


17

我试图根据字符串中有1的数目将某些数字的二进制字符串组合在一起。

这不起作用:

s = "0 1 3 7 8 9 11 15"
numbers = map(int, s.split())
binaries = [bin(x)[2:].rjust(4, '0') for x in numbers]

one_groups = dict.fromkeys(range(5), [])
for x in binaries:
    one_groups[x.count('1')] += [x]

预期的字典one_groups必须是

{0: ['0000'], 
 1: ['0001', '1000'], 
 2: ['0011', '1001'], 
 3: ['0111', '1011'], 
 4: ['1111']}

但是我明白了

{0: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 1: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 2: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 3: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 4: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111']}

到目前为止,唯一起作用的是如果我使用one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]而不是one_groups[x.count('1')] += [x]

但是为什么会这样呢?如果我没记错的话,不dict[key]应该返回该字典的值,类似于如何dict.get(key)工作?我看过这个线程,为什么用dict.get(key)而不是dict [key]?但这并没有回答我针对此特定情况的问题,因为我确定该程序并非旨在获取KeyError

我也尝试过,one_groups[x.count('1')].append(x)但这也不起作用。


8
getNone如果键不存在或没有提供任何默认值,则返回;而如果键不存在,则索引运算符[]将引发错误。
adnanmuttaleb

旁注,bin(x)[2:].rjust(4, '0')可以简化为'{:0>4b}'.format(x)
wjandrea

1
顺便说一句,它有助于使一个最小的可复制的例子。在这种情况下,您的制作binaries方式与问题无关,因此您只需提供其价值即可。
wjandrea

1
这回答了你的问题了吗?dict.fromkeys全部指向同一列表
Georgy

Answers:


24

问题是可变性:

one_groups = dict.fromkeys(range(5), [])-这会将与值相同的列表传递给所有键。因此,如果更改一个值,则全部更改。

基本上与说相同:

tmp = []
one_groups = dict.fromkeys(range(5), tmp)
del tmp

如果要使用新列表,则需要循环执行-显式for循环或dict理解:

one_groups = {key: [] for key in range(5)}

这个东西将为每个键“执行” [](等于list()),从而使值具有不同的列表。


为什么get起作用?因为您显式获取当前列表,但+创建了一个新的结果列表。它不会不管它是否one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]还是one_groups[x.count('1')] = one_groups[x.count('1')] + [x]-要紧的是,有+

我知道大家都怎么说的a+=b只是a=a+b,但实现可以有利于优化不同-在列表中的情况下,+=仅仅是.extend因为我们知道我们希望在当前的变量的结果,那么创建新的清单将是浪费内存。


啊,是的,明白了。我还记得当我想使用2D列表mylist = [[] * 5] * 5以及如何mylist = [[] for x in range(5)] * 5修复它时遇到类似的问题。为了快速澄清,据我所知,发生这种情况是因为变量指向该空列表的内存地址。这是否还意味着如果我使用基本体就不会出现问题?
SpectraXCD

1
是的,如果您使用原始类型,则可以解决该问题,但one_groups[x.count('1')] += [x]由于无法将列表添加到原始类型而中断 。更好的解决方案是改用defaultdict。
赫·莫卡登

4
具体来说,+调用__add__并返回一个新对象,而+=调用__iadd__并不需要返回一个新对象
njzk2

8

问题正在使用 one_groups = dict.fromkeys(range(5), [])

(这会将与值相同的列表传递给所有键。因此,如果更改一个值,则全部更改)


您可以改用以下方法: one_groups = {i:[] for i in range(5)}

(此操作将为每个键“执行” [](等于list()),从而使值具有不同的列表。)


6
您绝对正确,尽管做出解释会很有帮助。这两行之间的区别真的不是很明显。
西蒙·芬克

是的,这是我的坏事。抱歉
Hameda169

4

这是对dict fromkeys方法的帮助。

关于内置功能fromkeys的帮助:

Builtins.type实例的fromkeys(iterable,value = None,/)方法使用来自iterable的键并将值设置为value创建一个新字典

那就是说fromkeys会接受一个值,即使它是一个可调用的,它也会首先对其求值,然后将该值分配给所有dict键。

列表在Python中是可变的,因此它将分配相同的空列表引用,并且一项更改将影响所有列表。

改用defaultdict:

>>> from collections import defaultdict
>>> one_groups = defaultdict(list)
>>> for x in binaries:
      one_groups[x.count('1')] += [x]
>>> one_groups = dict(one_groups) # to stop default dict behavior

这将接受对不存在的键的分配,并且值将默认为空列表(在这种情况下)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.