Python 2.5での追加により、のメソッドcollections.defaultdict
の必要性が大幅に減少しました。この質問は私たちの集合教育のためのものです:dict
setdefault
setdefault
今日のPython2.6/ 2.7では、何がまだ役立つのでしょうか。- どのような一般的なユースケース
setdefault
が置き換えられましたcollections.defaultdict
か?
Python 2.5での追加により、のメソッドcollections.defaultdict
の必要性が大幅に減少しました。この質問は私たちの集合教育のためのものです:dict
setdefault
setdefault
今日のPython2.6/ 2.7では、何がまだ役立つのでしょうか。setdefault
が置き換えられましたcollections.defaultdict
か?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 )
私はよく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)
キーワード引数を取る関数のラッパーで引数を微調整するのに最適です。
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
私はそれを見てみたい.
ほとんどの回答が述べているように、キーが存在しない場合はデフォルト値を設定できますsetdefault
。defaultdict
ただし、 の使用例に関しては、ちょっとした注意点を指摘したいと思い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 は、再帰関数のキャッシュ要件をより適切に処理します。
Muhammad が言ったように、デフォルト値を設定したい場合があります。この良い例は、最初に入力され、次にクエリされるデータ構造です。
試してみてください。単語を追加するとき、サブノードが必要であるが存在しない場合は、トライを拡張するためにサブノードを作成する必要があります。単語の存在を照会する場合、欠落しているサブノードは、その単語が存在せず、作成されるべきではないことを示します。
defaultdict はこれを行うことができません。代わりに、get および setdefault メソッドを使用した通常の dict を使用する必要があります。
理論的に言えば、デフォルトを設定したい場合とそうでない場合がある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 オブジェクトが を取り除くという変更を予見することはできません。
defaultdict
over dict
( dict.setdefault
)の欠点の 1 つは、存在しないキーが与えられるたびdefaultdict
にオブジェクトが新しいアイテムを作成することです (たとえば、 , を使用)。また、クラスは一般的にクラスよりも一般的ではなく、IME をシリアル化するのがより困難です。==
print
defaultdict
dict
PS IMO 関数|オブジェクトを変更することを意図していないメソッドは、オブジェクトを変更すべきではありません。
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'])
【追記】大間違い!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))