collections.defaultdictはどのように機能しますか?


532

私はpythonのドキュメントで例を読みましたが、それでもこのメソッドの意味を理解できません。誰か助けてもらえますか?python docsの2つの例を示します

>>> 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
ところで、ユースケースにもよりますが、defaultdictへの入力default_factory = Noneが完了した後でdefaultdictを設定して読み取り専用で使用すること忘れないでくださいこの質問を参照してください。
Acumenus 2016年

回答:


598

通常、Pythonディクショナリは、KeyError現在ディクショナリにないキーを持つアイテムを取得しようとするとスローします。defaultdict対照的には、単にあなたがアクセスしようとする任意の項目(もちろん提供彼らはまだ存在していない)を作成します。このような「デフォルト」アイテムを作成するには、コンストラクターに渡す関数オブジェクトを呼び出します(より正確には、関数と型オブジェクトを含む任意の「呼び出し可能」オブジェクトです)。最初の例では、デフォルトのアイテムはを使用して作成されint()、整数オブジェクトを返します0。2番目の例では、デフォルトのアイテムはを使用して作成されlist()、新しい空のリストオブジェクトを返します。


4
d.get(key、default_val)を使用する場合と機能的に異なりますか?
アンバリーシュ

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 2013

13
または、単に「bar」を返したい場合:somedict = defaultdict(lambda: "bar")
Michael Scott Cuthbert

4行目は返された0それがあった場合は、整数をsomeddict = defaultdict(list)それが返されます[ ]。デフォルトの整数は0ですか?または[]デフォルトのリスト?
Gathide 2017年

どちらでもない。0は不変です-CPythonでは-5to からのすべての値256はキャッシュされたシングルトンですが、これは実装固有の動作です-どちらの場合も、int()またはで毎回新しいインスタンスが「作成」されますlist()。そうd[k].append(v)すれば、同じリストへの参照を辞書に入力しなくても機能し、defaultdictほとんど役に立たなくなります。これが動作でdefaultdictある場合、パラメータとしてラムダではなく値を使用します。(恐ろしい説明で申し訳ありません!)
wizzwizz4

93

defaultdict

「標準ディクショナリには、値を取得し、値が存在しない場合にデフォルトを確立するためのメソッドsetdefault()が含まれています。対照的defaultdictに、コンテナが初期化されるときに、呼び出し側にデフォルト(返される値)を指定させます。」

で定義されたダグ・ヘルマンの例でPythonの標準ライブラリ

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>, {})

** 2番目の引数として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

辞書は、後で名前(キー)で検索できるようにデータを保存する便利な方法です。キーは一意の不変オブジェクトである必要があり、通常は文字列です。辞書の値は何でもかまいません。多くのアプリケーションでは、値は整数や文字列などの単純な型です。

辞書内の値がコレクション(リスト、辞書など)である場合、より興味深いものになります。この場合、指定されたキーが初めて使用されるときに、値(空のリストまたは辞書)を初期化する必要があります。これは手動で比較的簡単に実行できますが、defaultdictタイプはこれらの種類の操作を自動化および簡素化します。defaultdictは通常のdictとまったく同じように機能しますが、引数をとらず、存在しないキーにデフォルト値を提供する関数(「デフォルトファクトリ」)で初期化されます。

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/

基本的に、パラメーターintおよびlistは、渡す関数です。Pythonは関数名を引数として受け入れることに注意してください。intはデフォルトでは0を返し、listは括弧で呼び出されると空のリストを返します。

通常の辞書では、あなたの例でをd[a]呼び出そうとすると、キー(m、s、i、pのみが存在し、キーaが初期化されていないため、エラー(KeyError)が発生します。ただし、defaultdictでは、関数名を引数として取り、初期化されていないキーを使用しようとすると、渡された関数を呼び出し、その戻り値を新しいキーの値として割り当てます。


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と、リストコンストラクターが1回だけ呼び出されdict.setdefault、リストコンストラクターを使用してより頻繁に呼び出されることです(ただし、本当に必要な場合は、これを回避するためにコードを書き直す場合があります)。

パフォーマンスの考慮があると主張する人もいますが、このトピックは地雷です。 この投稿は、たとえばdefaultdictを使用してもパフォーマンスが大幅に向上しないことを示しています。

IMO、defaultdictは、コードの利点よりも混乱を追加するコレクションです。私には役に立たないが、他の人は違うと思うかもしれない。


3

defaultdictツールは、Pythonのコレクションクラスのコンテナです。これは通常のディクショナリー(dict)コンテナーに似ていますが、1つの違いがあります。値フィールドのデータ型は初期化時に指定されます。

例えば:

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ステートメントの代わりに使用するのが最適だと思います。次のような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'
}

switchPythonで使用できるcaseステートメントはありません。を使用して同じことを達成でき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、キーが存在しない場合にスローされる生の辞書よりもかなり優れています。このことからdefaultdict、複雑なif-elif-elif-elseブロックを回避できるswitch caseステートメントに似ていることは明らかです。

このサイトから私に多くの印象を与えたもう1つの良い例は次のとおりです。

>>> 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})
>>>

以外のアイテムにアクセスしようeggsspamすると、カウントは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は次の場合にもkeyerrorを発生させる可能性があります:

    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.