71

次の2つの式は私と同じように見えます。どちらが望ましいですか?

data = [('a', 1), ('b', 1), ('b', 2)]

d1 = {}
d2 = {}

for key, val in data:
    # variant 1)
    d1[key] = d1.get(key, []) + [val]
    # variant 2)
    d2.setdefault(key, []).append(val)

結果は同じですが、どちらのバージョンがより優れているのでしょうか、それともよりPython的ですか?

個人的には、バージョン2を理解するのは難しいと思います。私にとって、setdefaultは理解するのが非常に難しいためです。私が正しく理解していれば、辞書で「key」の値を探します。使用できない場合は、dictに「[]」を入力し、値または「[]」への参照を返し、それに「val」を追加します参照。確かにスムーズですが、少なくとも(少なくとも私にとっては)直感的ではありません。

私の考えでは、バージョン1の方が理解しやすいです(利用可能な場合は「key」の値を取得し、そうでない場合は「[]」を取得してから、[val]で構成されるリストに参加し、結果を「key」に配置します。 )。しかし、より直感的に理解できますが、このバージョンではパフォーマンスが低下し、このすべてのリストが作成されるのではないかと心配しています。もう1つの欠点は、「d1」が式で2回発生することです。これは、エラーが発生しやすいことです。おそらくgetを使用したより良い実装がありますが、現在それは私にはわかりません。

私の推測では、バージョン2は、経験の浅い人にとっては理解するのが難しいですが、より高速であり、したがって好ましいと思います。意見?

4

8 に答える 8

38

あなたの2つの例は同じことをしますが、それは意味するものではgetありsetdefaultません。

2つの違いは、基本的d[key]に、リストを毎回指すように手動で設定するのに対し、リストが設定されていない場合にのみsetdefault自動的に設定することです。d[key]

2つの方法をできるだけ類似させて、実行しました

from timeit import timeit

print timeit("c = d.get(0, []); c.extend([1]); d[0] = c", "d = {1: []}", number = 1000000)
print timeit("c = d.get(1, []); c.extend([1]); d[0] = c", "d = {1: []}", number = 1000000)
print timeit("d.setdefault(0, []).extend([1])", "d = {1: []}", number = 1000000)
print timeit("d.setdefault(1, []).extend([1])", "d = {1: []}", number = 1000000)

そして得た

0.794723378711
0.811882272256
0.724429205999
0.722129751973

したがって、この目的setdefaultよりも約10%高速です。get

このget方法では、でできるよりも少ないsetdefaultことを行うことができます。KeyErrorキーを設定したくない場合でも、キーが存在しない場合(それが頻繁に発生する場合)にを回避するために使用できます。

'setdefault' dictメソッドのユースケースを参照してください。dict.get()メソッドは、2つのメソッドに関する詳細情報へのポインターを返します。

についてのスレッドsetdefaultは、ほとんどの場合、を使用したいと結論付けていますdefaultdict。についてのスレッドgetは、それが遅いと結論付けており、多くの場合、ダブルルックアップを実行するか、defaultdictを使用するか、エラーを処理する方が良いでしょう(辞書のサイズとユースケースによって異なります)。

于 2011-09-14T22:14:40.203 に答える
26

agfから受け入れられた答えは、likeとlikeを比較していません。後:

print timeit("d[0] = d.get(0, []) + [1]", "d = {1: []}", number = 10000)

d[0]10,000アイテムのリストが含まれていますが、後は​​次のようになります。

print timeit("d.setdefault(0, []) + [1]", "d = {1: []}", number = 10000)

d[0]単に[]です。つまり、d.setdefaultバージョンがに保存されているリストを変更することはありませんd。コードは実際には次のようになります。

print timeit("d.setdefault(0, []).append(1)", "d = {1: []}", number = 10000)

setdefault実際、欠陥のある例よりも高速です。

ここでの違いは、連結を使用して追加すると、リスト全体が毎回コピーされるためです(10,000個の要素が測定可能になり始めたらappend、リストの更新を使用すると、O(1)、つまり実質的に一定の時間で償却されます。

最後に、元の質問では考慮されていない他の2つのオプションがあります。defaultdictまたは、辞書をテストして、すでにキーが含まれているかどうかを確認します。

だから、仮定d3, d4 = defaultdict(list), {}

# variant 1 (0.39)
d1[key] = d1.get(key, []) + [val]
# variant 2 (0.003)
d2.setdefault(key, []).append(val)
# variant 3 (0.0017)
d3[key].append(val)
# variant 4 (0.002)
if key in d4:
    d4[key].append(val)
else:
    d4[key] = [val]

バリアント1は毎回リストをコピーするため、はるかに低速です。バリアント2は2番目に低速で、バリアント3は最速ですが、2.5より古いPythonが必要な場合は機能しません。また、バリアント4はバリアント3よりもわずかに低速です。 。

defaultdict可能であれば、バリアント3を使用し、完全に適合しない場合がある場所のオプションとしてバリアント4を使用すると思います。元のバリアントの両方を避けてください。

于 2011-09-15T08:03:43.820 に答える
14

これらの2つの用語を理解するのにまだ苦労している人のために、get()メソッドとsetdefault()メソッドの基本的な違いを教えてください-

シナリオ-1

root = {}
root.setdefault('A', [])
print(root)

シナリオ2

root = {}
root.get('A', [])
print(root)

シナリオ1では、出力は{'A': []}シナリオ2にあります{}

したがって、辞書に存在しsetdefault()ないキーを設定し、get()デフォルト値のみを提供しますが、辞書は変更しません。

ここで、これが役立つ場所に行きましょう。値がリストであるdict内の要素を検索していて、見つかった場合はそのリストを変更し、そうでない場合はそのリストを使用して新しいキーを作成するとします。

を使用してsetdefault()

def fn1(dic, key, lst):
    dic.setdefault(key, []).extend(lst)

を使用してget()

def fn2(dic, key, lst):
    dic[key] = dic.get(key, []) + (lst) #Explicit assigning happening here

タイミングを調べてみましょう-

dic = {}
%%timeit -n 10000 -r 4
fn1(dic, 'A', [1,2,3])

288nsかかりました

dic = {}
%%timeit -n 10000 -r 4
fn2(dic, 'A', [1,2,3])

128秒かかりました

したがって、これら2つのアプローチの間には非常に大きなタイミングの違いがあります。

于 2018-09-25T12:23:32.687 に答える
11

defaultdictモジュールで確認することをお勧めしcollectionsます。以下はあなたの例と同等です。

from collections import defaultdict

data = [('a', 1), ('b', 1), ('b', 2)]

d = defaultdict(list)

for k, v in data:
    d[k].append(v)

ここにはもっとあります。

于 2011-09-14T21:56:35.147 に答える
8

1.ここに良い例を示して説明します:http:
//code.activestate.com/recipes/66516-add-an-entry-to-a-dictionary-unless-the-entry-is-a/

口述。setdefaultの一般的な使用法
somedict.setdefault(somekey,[]).append(somevalue)

口述。典型的な使用法を取得します
theIndex[word] = 1 + theIndex.get(word,0)


2.詳細説明: http: //python.net/~goodger/projects/pycon/2007/idiomatic/handout.html

dict.setdefault()getまたはと同等set & getです。またはset if necessary then get。辞書キーの計算に費用がかかる場合や入力に時間がかかる場合は、特に効率的です。

dict.setdefault()の唯一の問題は、必要かどうかに関係なく、デフォルト値が常に評価されることです。これは、デフォルト値の計算にコストがかかる場合にのみ重要です。その場合は、defaultdictを使用してください。


3.最後に、違いが強調表示された公式ドキュメント http://docs.python.org/2/library/stdtypes.html

get(key[, default])
キーがディクショナリにある場合はキーの値を返し、そうでない場合はデフォルトを返します。defaultが指定されていない場合、デフォルトでNoneに設定されるため、このメソッドでKeyErrorが発生することはありません。

setdefault(key[, default])
キーが辞書にある場合は、その値を返します。そうでない場合は、デフォルト値のキーを挿入し、デフォルトを返します。デフォルトのデフォルトはNoneです。

于 2014-03-11T13:06:37.520 に答える
2

のロジックdict.getは次のとおりです。

if key in a_dict:
    value = a_dict[key] 
else: 
    value = default_value

例を見てみましょう:

In [72]: a_dict = {'mapping':['dict', 'OrderedDict'], 'array':['list', 'tuple']}
In [73]: a_dict.get('string', ['str', 'bytes'])
Out[73]: ['str', 'bytes']
In [74]: a_dict.get('array', ['str', 'byets'])
Out[74]: ['list', 'tuple']

のメカニズムsetdefaultは次のとおりです。

    levels = ['master', 'manager', 'salesman', 'accountant', 'assistant']
    #group them by the leading letter
    group_by_leading_letter = {}
    # the logic expressed by obvious if condition
    for level in levels:
        leading_letter = level[0]
        if leading_letter not in group_by_leading_letter:
            group_by_leading_letter[leading_letter] = [level]
        else:
            group_by_leading_letter[leading_letter].append(word)
    In [80]: group_by_leading_letter
    Out[80]: {'a': ['accountant', 'assistant'], 'm': ['master', 'manager'], 's': ['salesman']}

setdefault dictメソッドは、まさにこの目的のためのものです。上記のforループは、次のように書き直すことができます。

In [87]: for level in levels:
    ...:     leading = level[0]
    ...:     group_by_leading_letter.setdefault(leading,[]).append(level)
Out[80]: {'a': ['accountant', 'assistant'], 'm': ['master', 'manager'], 's': ['salesman']}

これは非常に単純です。つまり、null以外のリストが要素を追加するか、nullリストが要素を追加します。

defaultdictこれをさらに簡単にします。作成するには、dictの各スロットのデフォルト値を生成するためのタイプまたは関数を渡します。

from collections import defualtdict
group_by_leading_letter = defaultdict(list)
for level in levels:
    group_by_leading_letter[level[0]].append(level)
于 2018-04-18T01:52:11.950 に答える
1
In [1]: person_dict = {}

In [2]: person_dict['liqi'] = 'LiQi'

In [3]: person_dict.setdefault('liqi', 'Liqi')
Out[3]: 'LiQi'

In [4]: person_dict.setdefault('Kim', 'kim')
Out[4]: 'kim'

In [5]: person_dict
Out[5]: {'Kim': 'kim', 'liqi': 'LiQi'}

In [8]: person_dict.get('Dim', '')
Out[8]: ''

In [5]: person_dict
Out[5]: {'Kim': 'kim', 'liqi': 'LiQi'}
于 2015-06-17T02:47:15.307 に答える
1

この質問に対する厳密な答えはありません。それらは両方とも同じ目的を達成します。これらは両方とも、キーの欠落値を処理するために使用できます。私が見つけた唯一の違いは、setdefault()を使用すると、呼び出したキー(以前に辞書にない場合)が自動的に挿入されますが、get()では発生しません。次に例を示します 。Setdefault()

>>> myDict = {'A': 'GOD', 'B':'Is', 'C':'GOOD'} #(1)
>>> myDict.setdefault('C')  #(2)
'GOOD'
>>> myDict.setdefault('C','GREAT')  #(3)
'GOOD'
>>> myDict.setdefault('D','AWESOME') #(4)
'AWESOME'
>>> myDict #(5)
{'A': 'GOD', 'B': 'Is', 'C': 'GOOD', 'D': 'AWSOME'} 
>>> myDict.setdefault('E')
>>>

得る()

>>> myDict = {'a': 1, 'b': 2, 'c': 3}   #(1)
>>> myDict.get('a',0)   #(2)
1
>>> myDict.get('d',0)   #(3)
0
>>> myDict #(4)
{'a': 1, 'b': 2, 'c': 3}

これが私の結論です。デフォルト値の代入に関して、具体的にどれが最善であるかについての具体的な答えはありません。唯一の違いは、setdefault()が辞書にデフォルト値を持つ新しいキーを自動的に追加するのに対し、get()は追加しないことです。詳細については、こちらをご覧ください

于 2020-04-15T06:50:51.343 に答える