collections.defaultdict는 어떻게 작동합니까?


531

파이썬 문서에서 예제를 읽었지만 여전히이 방법의 의미를 알 수는 없습니다. 누군가 도울 수 있습니까? 파이썬 문서에서 두 가지 예가 있습니다.

>>> from collections import defaultdict

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
...     d[k] += 1
...
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...     d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

매개 변수 intlist무엇을위한?


15
BTW는 사용 사례에 따라 defaultdict 채우기를 마친 후에 설정하여 읽기 전용으로 사용할 defaultdict를 고정하는 것을 잊지 마십시오default_factory = None . 이 질문을 참조하십시오 .
Acumenus

답변:


598

일반적으로 파이썬 사전 KeyError은 현재 사전에없는 키로 항목을 얻으려고하면 a를 던집니다 . defaultdict달리 단순히 액세스하려고하는 것이 모든 항목 (물론 제공 그들은 아직 존재하지 않는)를 작성합니다. 이러한 "기본"항목을 만들려면 생성자에 전달하는 함수 객체를 호출합니다 (보다 정확하게는 함수 및 유형 객체를 포함하는 임의의 "호출 가능한"객체입니다). 첫 번째 예의 경우 기본 항목은을 사용하여 만들어지며 int()정수 객체가 반환됩니다 0. 두 번째 예의 경우 기본 항목은을 사용하여 만들어지며 list()새 빈 목록 객체가 반환됩니다.


4
d.get (key, default_val)을 사용하는 것과 기능적으로 다른가요?
Ambareesh

29
@Ambareesh d.get(key, default)는 사전을 수정하지 않습니다. 기본값을 반환하고 사전을 변경하지 않고 그대로 둡니다. defaultdict반면에 사전에 키가 없으면 사전에 키를 삽입 합니다. 이것은 큰 차이입니다. 이유를 이해하려면 질문의 예를 참조하십시오.
Sven Marnach

각 유형의 기본값이 무엇인지 어떻게 알 수 있습니까? int ()의 경우 0, list ()의 경우 []는 직관적이지만 더 복잡하거나 자체 정의 된 유형이있을 수도 있습니다.
Sean

1
@Sean defaultdict은 전달하는 생성자를 호출합니다. a 유형을 전달하면을 T사용하여 값이 구성됩니다 T(). 매개 변수를 전달하지 않고 모든 유형을 구성 할 수있는 것은 아닙니다. 이러한 유형을 구성하려면 래퍼 함수 또는 이와 비슷한 것이 필요합니다 functools.partial(T, arg1, arg2).
Sven Marnach

224

defaultdict사전에 키가 없으면 KeyError던져지는 대신 새 항목이 작성 됨을 의미합니다 . 이 새 항목의 유형은 defaultdict의 인수로 제공됩니다.

예를 들면 다음과 같습니다.

somedict = {}
print(somedict[3]) # KeyError

someddict = defaultdict(int)
print(someddict[3]) # print int(), thus 0

10
"이 새로운 쌍의 유형은 defaultdict의 주장에 의해 주어진다." 인수가 될 수 있습니다 어떤 단지 기능을 입력하지 - 호출 개체를. 예를 들어, foo가 "bar"를 리턴 한 함수 인 경우, foo는 기본 dict에 대한 인수로 사용될 수 있으며 존재하지 않는 키에 액세스 한 경우 해당 값은 "bar"로 설정됩니다.
lf215

13
아니면 그냥 "바"를 반환하려는 경우 : somedict = defaultdict (람다 : "바")
마이클 스캇 커스버트

네 번째 줄 0은 정수를 someddict = defaultdict(list)반환했습니다 [ ]. 0이 기본 정수입니까? 아니면 [] 기본 목록?
Gathide

둘 다. 0불변 - CPython과의 모든 값 -5에이 256싱글을 캐시하지만이 구현 고유의 동작입니다 - 두 경우 모두 새로운 인스턴스는 각 시간을 "창조"입니다 int()list(). 그렇게 d[k].append(v)하면 사전을 같은 목록에 대한 참조로 채우지 않고도 작업 할 수 defaultdict있어 거의 쓸모가 없게됩니다. 이것이 동작 인 defaultdict경우 람다가 아닌 값을 매개 변수로 사용합니다. (끔찍한 설명 죄송합니다!)
wizzwizz4

93

defaultdict

"표준 사전에는 값을 검색하고 값이 존재하지 않는 경우 기본값을 설정하기위한 setdefault () 메소드가 포함되어 있습니다. 반대로, defaultdict컨테이너가 초기화 될 때 호출자가 기본값 (반환 될 값)을 지정할 수있게합니다."

예제로 파이썬 표준 라이브러리 에서 Doug Hellmann이 정의한대로

defaultdict를 사용하는 방법

defaultdict 가져 오기

>>> from collections import defaultdict

defaultdict를 초기화하십시오

전달하여 초기화

첫 번째 인수로 호출 가능 (필수)

>>> d_int = defaultdict(int)
>>> d_list = defaultdict(list)
>>> def foo():
...     return 'default value'
... 
>>> d_foo = defaultdict(foo)
>>> d_int
defaultdict(<type 'int'>, {})
>>> d_list
defaultdict(<type 'list'>, {})
>>> d_foo
defaultdict(<function foo at 0x7f34a0a69578>, {})

** 두 번째 인수로 kwargs (선택 사항)

>>> d_int = defaultdict(int, a=10, b=12, c=13)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})

또는

>>> kwargs = {'a':10,'b':12,'c':13}
>>> d_int = defaultdict(int, **kwargs)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})

작동 원리

표준 사전의 하위 클래스와 마찬가지로 동일한 기능을 모두 수행 할 수 있습니다.

그러나 알 수없는 키를 전달하면 오류 대신 기본값을 반환합니다. 예를 들어 :

>>> d_int['a']
10
>>> d_int['d']
0
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12, 'd': 0})

기본값을 변경하려면 default_factory를 덮어 씁니다.

>>> d_int.default_factory = lambda: 1
>>> d_int['e']
1
>>> d_int
defaultdict(<function <lambda> at 0x7f34a0a91578>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0})

또는

>>> def foo():
...     return 2
>>> d_int.default_factory = foo
>>> d_int['f']
2
>>> d_int
defaultdict(<function foo at 0x7f34a0a0a140>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0, 'f': 2})

문제의 예

실시 예 1

int가 default_factory로 전달되었으므로 알 수없는 키는 기본적으로 0을 반환합니다.

이제 문자열이 루프에 전달되면 d에서 해당 알파벳의 수가 증가합니다.

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> d.default_factory
<type 'int'>
>>> for k in s:
...     d[k] += 1
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
>>> d
defaultdict(<type 'int'>, {'i': 4, 'p': 2, 's': 4, 'm': 1})

실시 예 2

목록이 default_factory로 전달되었으므로 알 수없는 (존재하지 않은) 키는 기본적으로 [] (즉, 목록)을 반환합니다.

이제 튜플 목록이 루프에 전달되면 d [color]에 값이 추가됩니다.

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> d.default_factory
<type 'list'>
>>> for k, v in s:
...     d[k].append(v)
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
>>> d
defaultdict(<type 'list'>, {'blue': [2, 4], 'red': [1], 'yellow': [1, 3]})

20

사전은 이름 (키)별로 나중에 검색 할 수 있도록 데이터를 저장하는 편리한 방법입니다. 키는 고유하고 변경 불가능한 객체 여야하며 일반적으로 문자열입니다. 사전의 값은 무엇이든 가능합니다. 많은 응용 프로그램에서 값은 정수 및 문자열과 같은 간단한 유형입니다.

사전의 값이 콜렉션 (목록, 딕트 등) 인 경우 더 흥미로워집니다.이 경우 지정된 키를 처음 사용할 때 값 (빈 목록 또는 dict)을 초기화해야합니다. 이 작업은 수동으로 수행하기가 비교적 쉽지만 defaultdict 유형은 이러한 종류의 작업을 자동화하고 단순화합니다. defaultdict는 일반 dict과 똑같이 작동하지만 인수를 사용하지 않고 존재하지 않는 키의 기본값을 제공하는 함수 ( "default factory")로 초기화됩니다.

defaultdict는 KeyError를 발생시키지 않습니다. 존재하지 않는 키는 기본 팩토리에서 리턴 한 값을 가져옵니다.

from collections import defaultdict
ice_cream = defaultdict(lambda: 'Vanilla')

ice_cream['Sarah'] = 'Chunky Monkey'
ice_cream['Abdul'] = 'Butter Pecan'

print(ice_cream['Sarah'])
>>>Chunky Monkey

print(ice_cream['Joe'])
>>>Vanilla

defaultdict를 사용하는 방법에 대한 또 다른 예는 복잡성을 줄일 수 있습니다.

from collections import defaultdict
# Time complexity O(n^2)
def delete_nth_naive(array, n):
    ans = []
    for num in array:
        if ans.count(num) < n:
            ans.append(num)
    return ans

# Time Complexity O(n), using hash tables.
def delete_nth(array,n):
    result = []
    counts = defaultdict(int)

    for i in array:
        if counts[i] < n:
            result.append(i)
            counts[i] += 1
    return result


x = [1,2,3,1,2,1,2,3]
print(delete_nth(x, n=2))
print(delete_nth_naive(x, n=2))

결론적으로, 사전이 필요하고 각 요소의 값이 기본값으로 시작해야 할 때마다 defaultdict를 사용하십시오.


18

여기에 defaultdicts에 대한 훌륭한 설명이 있습니다 : http://ludovf.net/blog/python-collections-defaultdict/

기본적으로 매개 변수 intlist 는 전달하는 함수입니다. 파이썬은 함수 이름을 인수로 받아들입니다. int 는 기본적으로 0을 반환하고 괄호와 함께 호출되면 list 는 빈 목록을 반환합니다.

일반적인 사전에서 귀하의 예제 d[a]에서을 호출하려고 하면 키 m, s, i 및 p 만 존재하고 키 a가 초기화되지 않았기 때문에 오류 (KeyError)가 발생합니다. 그러나 기본적으로 함수 이름을 인수로 사용합니다. 초기화되지 않은 키를 사용하려고하면 전달 된 함수를 호출하고 반환 값을 새 키의 값으로 지정합니다.


7

문제는 "작동 방식"에 관한 것이므로 일부 독자는 더 많은 볼트와 너트를보고 싶을 수도 있습니다. 구체적으로, 해당 방법은 __missing__(key)방법이다. https://docs.python.org/2/library/collections.html#defaultdict-objects를 참조 하십시오 .

보다 구체적으로,이 답변은 __missing__(key)실용적인 방법으로 활용하는 방법을 보여줍니다 : https://stackoverflow.com/a/17956989/1593924

'호출 가능'의 의미를 명확히하기 위해 대화식 세션이 있습니다 (2.7.6부터 v3에서도 작동해야 함).

>>> x = int
>>> x
<type 'int'>
>>> y = int(5)
>>> y
5
>>> z = x(5)
>>> z
5

>>> from collections import defaultdict
>>> dd = defaultdict(int)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd = defaultdict(x)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd['a']
0
>>> dd
defaultdict(<type 'int'>, {'a': 0})

이것이 가장 일반적인 defaultdict 사용이었습니다 (x 변수의 무의미한 사용 제외). 명시 적 기본값과 0으로 동일한 작업을 수행 할 수 있지만 간단한 값으로는 수행 할 수 없습니다.

>>> dd2 = defaultdict(0)

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    dd2 = defaultdict(0)
TypeError: first argument must be callable

대신 다음은 간단한 함수를 전달하기 때문에 작동합니다 (인수를 사용하지 않고 항상 0을 반환하는 이름없는 함수를 즉시 생성합니다).

>>> dd2 = defaultdict(lambda: 0)
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {})
>>> dd2['a']
0
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {'a': 0})
>>> 

그리고 다른 기본값으로 :

>>> dd3 = defaultdict(lambda: 1)
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {})
>>> dd3['a']
1
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {'a': 1})
>>> 

7

내 자신의 2 ¢ : 당신은 또한 defaultdict를 서브 클래스 화 할 수 있습니다 :

class MyDict(defaultdict):
    def __missing__(self, key):
        value = [None, None]
        self[key] = value
        return value

이것은 매우 복잡한 경우에 유용 할 수 있습니다.


4

모든 통화 대신 defaultdict사용하여 행동을 쉽게 모방 할 수 있습니다 .dict.setdefaultd[key]

다시 말해, 코드는 :

from collections import defaultdict

d = defaultdict(list)

print(d['key'])                        # empty list []
d['key'].append(1)                     # adding constant 1 to the list
print(d['key'])                        # list containing the constant [1]

다음과 같습니다.

d = dict()

print(d.setdefault('key', list()))     # empty list []
d.setdefault('key', list()).append(1)  # adding constant 1 to the list
print(d.setdefault('key', list()))     # list containing the constant [1]

유일한 차이점은을 사용 defaultdict하여 목록 생성자가 한 번만 호출 dict.setdefault되고 목록 생성자를 사용 하는 것이 더 자주 호출된다는 점입니다 (그러나 실제로 필요한 경우이를 피하기 위해 코드를 다시 작성할 수 있음).

일부는 성능 고려 사항이있을 수 있지만이 주제는 지뢰밭입니다. 이 게시물은 예를 들어 defaultdict를 사용하면 성능이 크게 향상되지 않음을 보여줍니다.

IMO, defaultdict는 코드에 대한 이점보다 혼란을 더하는 모음입니다. 나에게는 쓸모가 없지만 다른 사람들은 다르게 생각할 수 있습니다.


3

defaultdict 도구는 Python의 collections 클래스에있는 컨테이너입니다. 일반적인 사전 (dict) 컨테이너와 비슷하지만 한 가지 차이점이 있습니다. 값 필드의 데이터 유형은 초기화시 지정됩니다.

예를 들면 다음과 같습니다.

from collections import defaultdict

d = defaultdict(list)

d['python'].append("awesome")

d['something-else'].append("not relevant")

d['python'].append("language")

for i in d.items():

    print i

인쇄합니다 :

('python', ['awesome', 'language'])
('something-else', ['not relevant'])

"값 필드의 데이터 유형은 초기화시 지정됩니다": 올바르지 않습니다. 요소 팩토리 기능이 제공됩니다. list생성 할 객체의 유형이 아닌 결 측값을 채우기 위해 호출하는 함수는 다음과 같습니다 . 예를 들어, 기본값 1인을 사용하려면 lambda:1분명히 유형이 아닌 값을 사용 합니다.
asac

2

스위치 케이스 문 대신 가장 잘 사용되는 것 같습니다. 아래와 같이 switch case 문이 있다고 상상해보십시오.

option = 1

switch(option) {
    case 1: print '1st option'
    case 2: print '2nd option'
    case 3: print '3rd option'
    default: return 'No such option'
}

switch파이썬에는 사용 가능한 사례 가 없습니다 . 를 사용하여 동일한 결과를 얻을 수 있습니다 defaultdict.

from collections import defaultdict

def default_value(): return "Default Value"
dd = defaultdict(default_value)

dd[1] = '1st option'
dd[2] = '2nd option'
dd[3] = '3rd option'

print(dd[4])    
print(dd[5])    
print(dd[3])

다음을 인쇄합니다.

Default Value
Default Value
3rd option

위의 스 니펫 dd에는 키 4 또는 5가 없으므로 도우미 기능에서 구성한 기본값을 인쇄합니다. 이것은 KeyError키가 없으면 a 가 던져지는 원시 사전보다 훨씬 좋습니다 . 이것으로부터 defaultdict복잡한 if-elif-elif-else블록을 피할 수있는 스위치 케이스 문장과 더 유사합니다 .

이 사이트 에서 많은 감동을 얻은 또 하나의 좋은 예 는 다음과 같습니다.

>>> from collections import defaultdict
>>> food_list = 'spam spam spam spam spam spam eggs spam'.split()
>>> food_count = defaultdict(int) # default value of int is 0
>>> for food in food_list:
...     food_count[food] += 1 # increment element's value by 1
...
defaultdict(<type 'int'>, {'eggs': 1, 'spam': 7})
>>>

우리는 이외의 다른 항목에 액세스하려고하면 eggs그리고 spam우리는 0의 수를 얻을 것이다.


2

이 없으면 defaultdict보이지 않는 키에 새 값을 할당 할 수 있지만 수정할 수는 없습니다. 예를 들면 다음과 같습니다.

import collections
d = collections.defaultdict(int)
for i in range(10):
  d[i] += i
print(d)
# Output: defaultdict(<class 'int'>, {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9})

import collections
d = {}
for i in range(10):
  d[i] += i
print(d)
# Output: Traceback (most recent call last): File "python", line 4, in <module> KeyError: 0

2

다음과 같은 경우에도 defaultdict에서 키 오류가 발생할 수 있습니다.

    from collections import defaultdict
    d = defaultdict()
    print(d[3]) #raises keyerror

항상 defaultdict (int)와 같은 defaultdict에 인수를 제공해야합니다.


0

표준 사전에는 값을 검색하고 값이없는 경우 기본값을 설정하기위한 setdefault () 메소드가 포함되어 있습니다. 반대로 defaultdict를 사용하면 컨테이너가 초기화 될 때 호출자가 기본값을 미리 지정할 수 있습니다.

import collections

def default_factory():
    return 'default value'

d = collections.defaultdict(default_factory, foo='bar')
print 'd:', d
print 'foo =>', d['foo']
print 'bar =>', d['bar']

이것은 모든 키가 동일한 기본값을 갖는 것이 적절하다면 잘 작동합니다. 기본값이 목록, 집합 또는 int와 같은 값을 집계하거나 누적하는 데 사용되는 유형 인 경우 특히 유용합니다. 표준 라이브러리 문서에는 이러한 방식으로 defaultdict를 사용하는 몇 가지 예가 포함되어 있습니다.

$ python collections_defaultdict.py

d: defaultdict(<function default_factory at 0x100468c80>, {'foo': 'bar'})
foo => bar
bar => default value

0

한마디로 :

defaultdict(int) -int 인수는 값이 int 유형임을 나타냅니다.

defaultdict(list) -인수 목록은 값이 목록 유형임을 나타냅니다.


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