[英]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 版雖然對於沒有經驗的人來說更難掌握,但速度更快,因此更可取。 意見?
你的兩個例子做同樣的事情,但這並不意味着get
和setdefault
做。
兩者之間的區別基本上是每次手動設置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
因此,為此目的, setdefault
比get
快 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()
等價於get
或set & 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.