파이썬 : defaultdict의 defaultdict?


323

defaultdict(defaultdict(int))다음 코드를 작동시킬 수 있는 방법이 있습니까?

for x in stuff:
    d[x.a][x.b] += x.c_int

d요구에 따라 임시을 구축 할 x.ax.b요소.

나는 사용할 수있다 :

for x in stuff:
    d[x.a,x.b] += x.c_int

그러나 나는 사용할 수 없었습니다 :

d.keys()
d[x.a].keys()

6
비슷한 질문보기 파이썬에서 중첩 된 사전을 구현하는 가장 좋은 방법은 무엇입니까? . Wikipedia의 Autovivification관한 기사에도 유용한 정보가 있습니다.
martineau

답변:


571

예, 이런 식으로 :

defaultdict(lambda: defaultdict(int))

존재하지 않는 키에 액세스하려고 하면 defaultdict(이 경우는 lambda: defaultdict(int)) 의 인수 가 호출됩니다. 그것의 반환 값은이 키의 새로운 값으로 설정 될 것이며,이 경우 우리의 값은 d[Key_doesnt_exist]입니다 defaultdict(int).

이 마지막 defaultdict에서 키에 액세스하려고하면, 즉 d[Key_doesnt_exist][Key_doesnt_exist]0이 리턴됩니다 int(). 즉, 마지막 defaultdict 인수의 리턴 값입니다 .


7
잘 작동합니다! 이 구문의 합리적인 이유를 설명해 주시겠습니까?
조나단

37
@Jonathan : 예, 존재하지 않는 키에 액세스하려고 하면 defaultdict(이 경우는 lambda : defaultdict(int)) 의 인수 가 호출되고 반환 값은이 키의 새로운 값으로 설정됩니다. 우리의 경우는의 값이 d[Key_dont_exist]될 것입니다 defaultdict(int), 당신은 즉이 마지막 defaultdict에서 키에 액세스하려고하면 d[Key_dont_exist][Key_dont_exist]그것이 마지막의 인수의 반환 값은 0 리턴 defaultdictint(), 희망이 도움이되었다.
mouad

25
인수 defaultdict는 함수 여야합니다. defaultdict(int)사전이며, 사전 lambda: defaultdict(int)을 반환하는 함수입니다.
has2k1

27
@ has2k1 잘못되었습니다. defaultdict에 대한 인수는 호출 가능해야합니다. 람다는 호출 가능합니다.
Niels Bom

2
@RickyLevi, 당신이 그 일을하고 싶다면 그냥 말할 수 있습니다 : defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
darophi

51

defaultdict 생성자에 대한 매개 변수는 새 요소를 빌드하기 위해 호출되는 함수입니다. 람다를 사용하자!

>>> from collections import defaultdict
>>> d = defaultdict(lambda : defaultdict(int))
>>> print d[0]
defaultdict(<type 'int'>, {})
>>> print d[0]["x"]
0

Python 2.7부터 Counter를 사용 하는 더 나은 솔루션이 있습니다 .

>>> from collections import Counter
>>> c = Counter()
>>> c["goodbye"]+=1
>>> c["and thank you"]=42
>>> c["for the fish"]-=5
>>> c
Counter({'and thank you': 42, 'goodbye': 1, 'for the fish': -5})

일부 보너스 기능

>>> c.most_common()[:2]
[('and thank you', 42), ('goodbye', 1)]

자세한 정보는 PyMOTW-콜렉션-컨테이너 데이터 유형Python 문서-콜렉션을 참조하십시오.


5
여기서 원을 완성하기 위해 , 원래 제기 된 문제를 구체적으로 다루기 d = defaultdict(lambda : Counter())보다는 사용하고 싶을 것 d = defaultdict(lambda : defaultdict(int))입니다.
gumption 2018 년

3
@gumption 당신은 d = defaultdict(Counter())이 경우에 람다를 사용할 필요가 없습니다
Deb

3
@Deb 약간의 오류가 있습니다-내부 괄호를 제거하여 Counter객체 대신 호출 가능 항목을 전달하십시오 . 즉 :d = defaultdict(Counter)
딜런 데이비스

29

사용하는 것이 조금 더 우아하다는 것을 알았습니다 partial.

import functools
dd_int = functools.partial(defaultdict, int)
defaultdict(dd_int)

물론 이것은 람다와 동일합니다.


1
부분적으로도 람다보다 재귀 적으로 적용될 수 있기 때문에 람다보다 낫습니다.
Campi

@Campi 재귀 응용 프로그램에는 부분적으로 필요하지 않습니다. AFAICT
Clément

10

참고로 다음을 defaultdict통해 일반 중첩 팩토리 메소드 를 구현할 수 있습니다 .

from collections import defaultdict
from functools import partial
from itertools import repeat


def nested_defaultdict(default_factory, depth=1):
    result = partial(defaultdict, default_factory)
    for _ in repeat(None, depth - 1):
        result = partial(defaultdict, result)
    return result()

깊이는에 정의 된 유형 default_factory이 사용 되기 전에 중첩 된 사전의 수를 정의합니다 . 예를 들면 다음과 같습니다.

my_dict = nested_defaultdict(list, 3)
my_dict['a']['b']['c'].append('e')

사용 예를들 수 있습니까? 내가 예상 한대로 작동하지 않습니다. ndd = nested_defaultdict(dict) .... ndd['a']['b']['c']['d'] = 'e'던지기KeyError: 'b'
David Marx

David, 여러분은 예제 3에서 사전의 깊이를 정의해야합니다 (default_factory를 사전으로 정의한 것처럼. nested_defaultdict (dict, 3)가 도움이됩니다)
Campi

감사합니다! 내가 알았던 한 가지는 이것이 default_dict at을 생성한다는 것 depth=0입니다. 호출시 깊이를 알 수없는 경우 항상 바람직하지 않을 수도 있습니다. if not depth: return default_factory()더 우아한 솔루션이 있지만 함수 상단에 line을 추가하여 쉽게 수정할 수 있습니다.
Brendan

9

이전 답변에서는 2 단계 또는 n 단계를 만드는 방법을 설명했습니다 defaultdict. 어떤 경우에는 무한한 것을 원합니다.

def ddict():
    return defaultdict(ddict)

용법:

>>> d = ddict()
>>> d[1]['a'][True] = 0.5
>>> d[1]['b'] = 3
>>> import pprint; pprint.pprint(d)
defaultdict(<function ddict at 0x7fcac68bf048>,
            {1: defaultdict(<function ddict at 0x7fcac68bf048>,
                            {'a': defaultdict(<function ddict at 0x7fcac68bf048>,
                                              {True: 0.5}),
                             'b': 3})})

1
나는 이것을 좋아한다. 엄청나게 간단하지만 매우 유용합니다. 감사!
rosstex

6

다른 사람들은 다음을 작동시키는 방법에 대한 귀하의 질문에 올바르게 대답했습니다.

for x in stuff:
    d[x.a][x.b] += x.c_int

다른 방법은 키에 튜플을 사용하는 것입니다.

d = defaultdict(int)
for x in stuff:
    d[x.a,x.b] += x.c_int
    # ^^^^^^^ tuple key

이 방법의 장점은 간단하고 쉽게 확장 할 수 있다는 것입니다. 3 단계 깊이의 매핑이 필요한 경우 키에 3 개의 항목 튜플을 사용하십시오.


4
이 솔루션은 모든 키를 튜플의 첫 번째 요소로 xa가 있는지 확인하기 위해 모든 키를 조사해야하기 때문에 모든 d [xa]를 얻는 것이 간단하지 않다는 것을 의미합니다.
Matthew Schinckel

5
3 레벨을 중첩하려면 3 레벨로 정의하십시오. d = defaultdict (lambda : defaultdict (lambda : defaultdict (int)))
Matthew Schinckel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.