216

Python 2.5での追加により、のメソッドcollections.defaultdictの必要性が大幅に減少しました。この質問は私たちの集合教育のためのものです:dictsetdefault

  1. setdefault今日のPython2.6/ 2.7では、何がまだ役立つのでしょうか。
  2. どのような一般的なユースケースsetdefaultが置き換えられましたcollections.defaultdictか?
4

18 に答える 18

235

dictを埋める前にdefaultdictデフォルトを設定するのに役立ち、 dict を埋めている間または埋めた後にデフォルトを設定するのに役立ちます。setdefault

おそらく最も一般的な使用例: アイテムのグループ化 (未ソートのデータで、それ以外の場合は を使用itertools.groupby)

# really verbose
new = {}
for (key, value) in data:
    if key in new:
        new[key].append( value )
    else:
        new[key] = [value]


# easy with setdefault
new = {}
for (key, value) in data:
    group = new.setdefault(key, []) # key might exist already
    group.append( value )


# even simpler with defaultdict 
from collections import defaultdict
new = defaultdict(list)
for (key, value) in data:
    new[key].append( value ) # all keys have a default already

dict を作成した後、特定のキーが存在することを確認したい場合があります。defaultdict明示的なアクセスでのみキーを作成するため、この場合は機能しません。多くのヘッダーで HTTP っぽいものを使用していると思います-いくつかはオプションですが、それらのデフォルトが必要です:

headers = parse_headers( msg ) # parse the message, get a dict
# now add all the optional headers
for headername, defaultvalue in optional_headers:
    headers.setdefault( headername, defaultvalue )
于 2010-08-14T14:05:51.163 に答える
36

私はよくsetdefault、この関数のように、キーワード引数 dict に使用します。

def notify(self, level, *pargs, **kwargs):
    kwargs.setdefault("persist", level >= DANGER)
    self.__defcon.set(level, **kwargs)
    try:
        kwargs.setdefault("name", self.client.player_entity().name)
    except pytibia.PlayerEntityNotFound:
        pass
    return _notify(level, *pargs, **kwargs)

キーワード引数を取る関数のラッパーで引数を微調整するのに最適です。

于 2010-08-14T15:01:17.300 に答える
19

defaultdictデフォルト値が新しいリストのように静的な場合は素晴らしいですが、動的な場合はそれほどではありません。

たとえば、文字列を一意の int にマップするための辞書が必要です。defaultdict(int)デフォルト値として常に 0 を使用します。同様に、defaultdict(intGen())常に 1 を生成します。

代わりに、通常の dict を使用しました。

nextID = intGen()
myDict = {}
for lots of complicated stuff:
    #stuff that generates unpredictable, possibly already seen str
    strID = myDict.setdefault(myStr, nextID())

dict.get(key, nextID())後で同様にこれらの値を参照できるようにする必要があるため、 では不十分であることに注意してください。

intGenは、int を自動的にインクリメントしてその値を返す、私が作成した小さなクラスです。

class intGen:
    def __init__(self):
        self.i = 0

    def __call__(self):
        self.i += 1
    return self.i

誰かがこれを行う方法を持っているなら、defaultdict私はそれを見てみたい.

于 2011-05-30T07:54:57.870 に答える
14

ほとんどの回答が述べているように、キーが存在しない場合はデフォルト値を設定できますsetdefaultdefaultdictただし、 の使用例に関しては、ちょっとした注意点を指摘したいと思いsetdefaultます。Python インタープリターが実行setdefaultされると、辞書にキーが存在する場合でも、常に関数の 2 番目の引数が評価されます。例えば:

In: d = {1:5, 2:6}

In: d
Out: {1: 5, 2: 6}

In: d.setdefault(2, 0)
Out: 6

In: d.setdefault(2, print('test'))
test
Out: 6

ご覧のとおりprint、辞書には 2 が既に存在していましたが、 も実行されました。setdefaultこれは、たとえば のような最適化に使用する予定がある場合に特に重要になりますmemoization。の 2 番目の引数として再帰関数呼び出しを追加するsetdefaultと、Python は常に関数を再帰的に呼び出すため、パフォーマンスが向上しません。

メモ化が言及されたので、メモ化で関数を拡張することを検討している場合は、 functools.lru_cache デコレーターを使用することをお勧めします。lru_cache は、再帰関数のキャッシュ要件をより適切に処理します。

于 2018-07-04T23:25:20.603 に答える
9

Muhammad が言ったように、デフォルト値を設定したい場合があります。この良い例は、最初に入力され、次にクエリされるデータ構造です。

試してみてください。単語を追加するとき、サブノードが必要であるが存在しない場合は、トライを拡張するためにサブノードを作成する必要があります。単語の存在を照会する場合、欠落しているサブノードは、その単語が存在せず、作成されるべきではないことを示します。

defaultdict はこれを行うことができません。代わりに、get および setdefault メソッドを使用した通常の dict を使用する必要があります。

于 2011-06-09T03:49:59.803 に答える
5

理論的に言えば、デフォルトを設定したい場合とそうでない場合があるsetdefault場合でも便利です。実生活では、そのようなユースケースに出くわしたことはありません。

ただし、興味深い使用例が標準ライブラリ (Python 2.6、_threadinglocal.py) から出てきます。

>>> mydata = local()
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]

using__dict__.setdefaultはかなり便利なケースだと思います。

編集:たまたま、これは標準ライブラリの唯一の例であり、コメントにあります。の存在を正当化するには十分ではないかもしれませんsetdefault。それでも、ここに説明があります:

オブジェクトは、その属性を属性に格納し__dict__ます。たまたま、__dict__属性はオブジェクトの作成後いつでも書き込み可能です。また、辞書ではありませんdefaultdict__dict__一般的なケースでオブジェクトがasを持つことは賢明ではありません。これは、defaultdict各オブジェクトがすべての有効な識別子を属性として持つことになるためです。__dict__.setdefaultしたがって、有用でないと見なされた場合に完全に削除することを除いて、Python オブジェクトが を取り除くという変更を予見することはできません。

于 2010-08-14T14:52:49.253 に答える
3

defaultdictover dict( dict.setdefault)の欠点の 1 つは、存在しないキーが与えられるたびdefaultdictにオブジェクトが新しいアイテムを作成することです (たとえば、 , を使用)。また、クラスは一般的にクラスよりも一般的ではなく、IME をシリアル化するのがより困難です。==printdefaultdictdict

PS IMO 関数|オブジェクトを変更することを意図していないメソッドは、オブジェクトを変更すべきではありません。

于 2016-12-03T04:58:42.060 に答える
2

setdefault の有用性を示す例を次に示します。

"""
d = {}
# To add a key->value pair, do the following:
d.setdefault(key, []).append(value)

# To retrieve a list of the values for a key
list_of_values = d[key]

# To remove a key->value pair is still easy, if
# you don't mind leaving empty lists behind when
# the last value for a given key is removed:
d[key].remove(value)

# Despite the empty lists, it's still possible to 
# test for the existance of values easily:
if d.has_key(key) and d[key]:
    pass # d has some values for key

# Note: Each value can exist multiple times!
"""
e = {}
print e
e.setdefault('Cars', []).append('Toyota')
print e
e.setdefault('Motorcycles', []).append('Yamaha')
print e
e.setdefault('Airplanes', []).append('Boeing')
print e
e.setdefault('Cars', []).append('Honda')
print e
e.setdefault('Cars', []).append('BMW')
print e
e.setdefault('Cars', []).append('Toyota')
print e

# NOTE: now e['Cars'] == ['Toyota', 'Honda', 'BMW', 'Toyota']
e['Cars'].remove('Toyota')
print e
# NOTE: it's still true that ('Toyota' in e['Cars'])
于 2014-09-21T21:05:41.227 に答える
0

【追記】大間違い!setdefault は常に long_computation をトリガーし、Python は熱心です。

タトルの答えを拡張します。私にとって最良のユースケースはキャッシュメカニズムです。それ以外の:

if x not in memo:
   memo[x]=long_computation(x)
return memo[x]

これは 3 行と 2 ~ 3 回の検索を消費するため、喜んで次のように記述します

return memo.setdefault(x, long_computation(x))
于 2016-03-16T09:18:48.723 に答える