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 [key] 대신 dict.get (key)? 그러나이 특정 사례에 대한 내 질문에 대답하지 못했습니다. 프로그램이KeyError

나는 또한 시도 one_groups[x.count('1')].append(x)했지만 이것이 작동하지 않습니다.


8
getNone키가 존재하지 않거나 제공된 기본값이면 키를 반환 하고 키가 없으면 인덱스 연산자 []가 오류를 발생시킵니다.
adnanmuttaleb

사이드 노트 bin(x)[2:].rjust(4, '0')는로 단순화 할 수 있습니다 '{:0>4b}'.format(x).
wjandrea

1
BTW는 최소한의 재현 가능한 예 를 만드는 데 도움이됩니다 . 이 경우 어떻게 만드는지 binaries가 질문과 관련이 없으므로 그 가치를 제공 할 수 있습니다.
wjandrea

1
이것이 귀하의 질문에 대답합니까? dict.fromkeys는 모두 같은 목록을 가리 킵니다
Georgy

답변:


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를 사용하는 것입니다.
Fakher Mokadem

4
특히, +통화 __add__반환 새로운 객체 반면, +=통화 __iadd__, 새로운 객체를 반환 할 필요가 없습니다
njzk2

8

문제는 one_groups = dict.fromkeys(range(5), [])

(이것은 모든 키에 값과 동일한 목록을 전달합니다. 따라서 하나의 값을 변경하면 모두 변경됩니다)


대신 이것을 사용할 수 있습니다 : one_groups = {i:[] for i in range(5)}

(이것은 모든 키에 대해 "실행"[] (list ()와 같음) 따라서 다른 목록으로 값을 만듭니다.)


6
설명이 정말 도움이 될지라도 당신은 절대적으로 옳습니다. 두 선의 차이점이 무엇인지는 분명하지 않습니다.
Simon Fink

그래, 나쁘다 미안
Hameda169

4

이것은 dict의 fromkeys방법 에 대한 도움말입니다 .

키의 내장 기능에 대한 도움말 :

frominkeys (iterable, value = None, /) builtins.type 인스턴스의 메소드 iterable의 키를 사용하고 값을 value로 설정하여 새 사전을 만듭니다.

즉, fromkeys는 값을 받아들이고 호출 가능하더라도 먼저 평가 한 다음 해당 값을 모든 dict 키에 할당합니다.

리스트는 파이썬에서 변경 가능하므로 동일한 빈리스트 참조를 할당하고 한 번의 변경으로 모든 것에 영향을 미칩니다.

대신 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

이것은 존재하지 않는 키에 대한 할당을 허용하며 값은 기본적으로 빈 목록 (이 경우)으로 설정됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.