簡體   English   中英

python dict:get vs setdefault

[英]python dict: get vs setdefault

以下兩個表達對我來說似乎是等價的。 哪個更可取?

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)

結果是一樣的,但哪個版本更好或更像是 Pythonic?

我個人覺得第 2 版更難理解,對我來說 setdefault 很難掌握。 如果我理解正確,它會在字典中查找“key”的值,如果不可用,則在字典中輸入“[]”,返回對該值或“[]”的引用,並將“val”附加到該值參考。 雖然肯定很流暢,但它至少不直觀(至少對我而言)。

在我看來,版本 1 更容易理解(如果可用,獲取“key”的值,如果沒有,獲取“[]”,然后加入由 [val] 組成的列表並將結果放入“key” )。 但是雖然更直觀地理解,但我擔心這個版本的性能較差,所有這些列表都在創建。 另一個缺點是“d1”在表達式中出現兩次,很容易出錯。 使用 get 可能有更好的實現,但目前我無法理解。

我的猜測是第 2 版雖然對於沒有經驗的人來說更難掌握,但速度更快,因此更可取。 意見?

你的兩個例子做同樣的事情,但這並不意味着getsetdefault做。

兩者之間的區別基本上是每次手動設置d[key]指向列表,而setdefault僅在未設置時自動將d[key]設置為列表。

使這兩種方法盡可能相似,我跑了

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

因此,為此目的, setdefaultget快 10% 左右。

get方法可以讓你比你可以用setdefault 即使您不想設置密鑰,您也可以使用它來避免在密鑰不存在時出現KeyError (如果這是經常發生的事情)。

有關這兩種方法的更多信息,請參閱“setdefault”dict 方法dict.get() 方法返回一個指針的用例

關於setdefault的線程得出的結論是,大多數情況下,您希望使用defaultdict 關於get的線程得出結論,它很慢,而且通常最好(速度明智)進行雙重查找、使用 defaultdict 或處理錯誤(取決於字典的大小和您的用例)。

來自 agf 的公認答案不是與同類比較。 后:

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),即有效地恆定時間。

最后,原始問題中沒有考慮另外兩個選項: 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 是第二慢的,變體 3 是最快的,但如果您需要 2.5 以上的 Python 則無法工作,變體 4 僅比變體 3 慢一點.

如果可以,我會說使用變體 3,將變體 4 作為選項用於那些defaultdict不完全合適的偶爾地方。 避免使用兩種原始變體。

您可能想查看collections模塊中的defaultdict 以下相當於您的示例。

from collections import defaultdict

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

d = defaultdict(list)

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

還有更多的在這里

對於那些仍在努力理解這兩個術語的人,讓我告訴您 get() 和 setdefault() 方法之間的基本區別 -

場景一

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

場景 2

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

在場景 1 中輸出將是{'A': []}而在場景 2 {}

所以setdefault()在 dict 中設置不存在的鍵,而get()只為您提供默認值但它不會修改字典。

現在讓我們來看看這將是有用的 - 假設您正在字典中搜索一個元素,其值為一個列表,並且您想要修改該列表,如果找到,則使用該列表創建一個新鍵。

使用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])

耗時 288 納秒

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

花了 128 秒

因此,這兩種方法之間存在非常大的時序差異。

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()等價於getset & get 或者set if necessary then get 如果您的字典鍵計算成本高或鍵入時間長,則特別有效。

dict.setdefault() 的唯一問題是默認值總是被評估,無論是否需要。 僅當默認值計算成本高時才重要 在這種情況下,請使用 defaultdict。


3. 最后突出顯示不同的官方文檔http://docs.python.org/2/library/stdtypes.html

get(key[, default])
如果鍵在字典中,則返回鍵的值,否則返回默認值。 如果未給出默認值,則默認為 None,因此此方法永遠不會引發 KeyError。

setdefault(key[, default])
如果鍵在字典中,則返回其值。 如果沒有,插入值為 default 的並返回默認值。 默認默認為無。

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']}

這很簡單,意味着非空列表追加元素或空列表追加元素。

defaultdict ,這使得這更容易。 要創建一個,你傳遞一個類型或函數來為字典中的每個插槽生成默認值:

from collections import defualtdict
group_by_leading_letter = defaultdict(list)
for level in levels:
    group_by_leading_letter[level[0]].append(level)
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'}

這個問題沒有嚴格的答案。 它們都實現了相同的目的。 它們都可以用來處理鍵上的缺失值。 我發現的唯一區別是,使用 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() 不會。 欲了解更多信息,請到這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM