の追加 collections.defaultdict
Python 2.5で、dict
のsetdefault
メソッドの必要性が大幅に減少しました。この質問は私たちの集団教育に対するものです:
- 今
setdefault
でもPython 2.6 / 2.7で何が役に立つのですか? setdefault
に取って代わられた人気のあるユースケースは何collections.defaultdict
ですか?
の追加 collections.defaultdict
Python 2.5で、dict
のsetdefault
メソッドの必要性が大幅に減少しました。この質問は私たちの集団教育に対するものです:
setdefault
でもPython 2.6 / 2.7で何が役に立つのですか?setdefault
に取って代わられた人気のあるユースケースは何collections.defaultdict
ですか?回答:
あなたは言うことができるdefaultdict
設定のデフォルト値のために有用である辞書を充填する前にし、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
辞書を作成した後、特定のキーが存在することを確認したい場合があります。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 )
defaultdict
。最初の段落であなたの意味の例を挙げていただけますか?
setdefault
ます。defaultdict
一方、A は、すべてdefaultvalues
が等しくない場合(つまり、一部が等しい場合0
と一部が等しい場合)には機能しません[]
。
headers = dict(optional_headers)
た理由です。デフォルト値がすべて等しくない場合。最終的な結果は、最初にHTTPヘッダーを取得してから、取得していないヘッダーのデフォルトを設定した場合と同じです。そして、すでに持っているなら、それはかなり使いやすいですoptional_headers
。私の与えられた2ステップのコードを試して、それをあなたのコードと比較してください。
new.setdefault(key, []).append(value)
defaultdict
は奇妙であることがわかります(つまり、setdefault
今のユースケースはどこですか?)。また、IMO ChainMap
のhttp
例をより適切に処理します。
私は通常setdefault
、この関数などのキーワード引数辞書に使用します。
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を生成します。
代わりに、通常の辞書を使用しました:
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
私はそれを見てみたいです。
intGen
てitertools.count().next
。
nextID()
の値はmyDict.setdefault()
、それが返す値がとして使用されていない場合でも、呼び出されるたびに増分されますstrID
。これはどういうわけか無駄に思わsetdefault()
れ、一般的に私が気に入らない点の1つを示しています。つまり、default
実際に使用されるかどうかにかかわらず、常に引数を評価します。
defaultdict
:myDict = defaultdict(lambda: nextID())
。その後、strID = myDict[myStr]
ループで。
myDict = defaultdict(nextID)
ですか?
ほとんどの回答の状態として、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は、再帰関数のキャッシュ要件をより適切に処理します。
理論的には、デフォルトを設定したいsetdefault
場合と設定しない場合がある場合でも、これは便利です。実際には、そのようなユースケースに遭遇したことはありません。
ただし、興味深いユースケースが標準ライブラリ(Python 2.6、_threadinglocal.py)から登場します。
>>> mydata = local()
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]
使用するの__dict__.setdefault
はかなり便利なケースだと思います。
編集:たまたま、これは標準ライブラリの唯一の例であり、コメントにあります。したがって、の存在を正当化するのに十分ではない場合がありsetdefault
ます。それでも、ここに説明があります:
オブジェクトは属性に属性を格納し__dict__
ます。偶然にも、__dict__
属性はオブジェクトの作成後いつでも書き込み可能です。また、辞書ではありませんdefaultdict
。一般的な場合のオブジェクトが__dict__
として持つことは賢明ではありません。これはdefaultdict
、各オブジェクトがすべての正当な識別子を属性として持つためです。そのため、Pythonオブジェクトが変更されることを予測できません__dict__.setdefault
。それが役に立たないと見なされた場合は、完全に削除することは別として。
__dict__
は、実装でdict
はなくであるということdefaultdict
です。
setdefault
Pythonにとどまることを気にしませんが、今ではほとんど役に立たないことを知りたいと思っています。
setdefault
存在するかもしれないし存在しないかもしれないキーを介して辞書に割り当てていることを明示し、それが存在しない場合はデフォルト値で作成したい:例えばd.setdefault(key,[]).append(value)
。プログラムの他の場所で、alist=d[k]
kが計算される場所で実行し、kがdにない場合に例外をスローする必要があります(defaultdictで必要になるassert k in d
か、場合によってはif not ( k in d): raise KeyError
欠点の1つdefaultdict
以上は、dict
(dict.setdefault
)ということであるdefaultdict
オブジェクトが新しいアイテムの作成毎回の非既存のキーが与えられた(例えばとされ==
、print
)。また、defaultdict
クラスは一般的にクラスほど一般的ではdict
なく、IMEをシリアル化するのがより困難です。
PS IMO関数|オブジェクトを変更することを意図していないメソッドは、オブジェクトを変更しないでください。
defaultdict(lambda l=[]: l)
代わりに同じように簡単に行うことができます。
以下に、その有用性を示すための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'])
私は受け入れられた回答を書き直し、初心者のためにそれを容易にしました。
#break it down and understand it intuitively.
new = {}
for (key, value) in data:
if key not in new:
new[key] = [] # this is core of setdefault equals to new.setdefault(key, [])
new[key].append(value)
else:
new[key].append(value)
# easy with setdefault
new = {}
for (key, value) in data:
group = new.setdefault(key, []) # it is new[key] = []
group.append(value)
# even simpler with defaultdict
new = defaultdict(list)
for (key, value) in data:
new[key].append(value) # all keys have a default value of empty list []
さらに、私は参照としてメソッドを分類しました:
dict_methods_11 = {
'views':['keys', 'values', 'items'],
'add':['update','setdefault'],
'remove':['pop', 'popitem','clear'],
'retrieve':['get',],
'copy':['copy','fromkeys'],}
これを取得し、辞書にデフォルト(!!!)を設定するとき、私は頻繁にsetdefaultを使用します。やや一般的にos.environ辞書:
# Set the venv dir if it isn't already overridden:
os.environ.setdefault('VENV_DIR', '/my/default/path')
簡潔に言うと、これは次のようになります。
# Set the venv dir if it isn't already overridden:
if 'VENV_DIR' not in os.environ:
os.environ['VENV_DIR'] = '/my/default/path')
結果の変数を使用することもできます。
venv_dir = os.environ.setdefault('VENV_DIR', '/my/default/path')
しかし、それはdefaultdictsが存在する前よりも必要ではありません。
私が偶然見つけた1つの非常に重要なユースケース: dict.setdefault()
単一の正規オブジェクトのみが必要な場合(たまたま同じになる複数のオブジェクトではない)のマルチスレッドコードに最適です。
たとえば(Int)Flag
、Python 3.6.0のEnumにはバグ(Int)Flag
があります。1つの複合メンバーに対して複数のスレッドが競合している場合、複数になる可能性があります。
from enum import IntFlag, auto
import threading
class TestFlag(IntFlag):
one = auto()
two = auto()
three = auto()
four = auto()
five = auto()
six = auto()
seven = auto()
eight = auto()
def __eq__(self, other):
return self is other
def __hash__(self):
return hash(self.value)
seen = set()
class cycle_enum(threading.Thread):
def run(self):
for i in range(256):
seen.add(TestFlag(i))
threads = []
for i in range(8):
threads.append(cycle_enum())
for t in threads:
t.start()
for t in threads:
t.join()
len(seen)
# 272 (should be 256)
解決策はsetdefault()
、計算された複合メンバーを保存する最後のステップとして使用することです。別のメンバーが既に保存されている場合は、新しいメンバーの代わりに使用され、一意のEnumメンバーが保証されます。
[編集] 非常に間違っている!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))
long_computation(x)
場合のみ呼び出されますx not in memo
。一方、2番目でlong_computation(x)
は、常に呼び出されます。割り当てのみが条件付きであり、同等のコードsetdefault
はv = long_computation(x)
/ if x not in memo:
/のようになりますmemo[x] = v
。
私はここに与えられた答えが好きです:
http://stupidpythonideas.blogspot.com/2013/08/defaultdict-vs-setdefault.html
要するに、(パフォーマンスが重要ではないアプリでの)決定は、空のキーの検索を下流で処理する方法(つまり、 KeyError
デフォルト値)に基づいて行う必要があります。
の別の使用例setdefault()
は、上書きしたくない場合です既に設定されているキーの値を。defaultdict
上書きしますが、上書きしsetdefault()
ません。ネストされた辞書の場合、現在のサブ辞書を削除したくないため、キーがまだ設定されていない場合にのみデフォルトを設定したい場合がよくあります。これはを使用する場合ですsetdefault()
。
の例defaultdict
:
>>> from collection import defaultdict()
>>> foo = defaultdict()
>>> foo['a'] = 4
>>> foo['a'] = 2
>>> print(foo)
defaultdict(None, {'a': 2})
setdefault
上書きしません:
>>> bar = dict()
>>> bar.setdefault('a', 4)
>>> bar.setdefault('a', 2)
>>> print(bar)
{'a': 4}