[英]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.