以下两个表达式似乎与我相同。哪个更好?
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”的值,如果不可用,则在“dict”中输入“[]”,返回对值或“[]”的引用,并在其中附加“val”参考。虽然顺利但它至少不是直观的(至少对我而言)。
在我看来,版本1更容易理解(如果可用,获取“key”的值,如果没有,获取“[]”,然后加入由[val]组成的列表并将结果放入“键”)。但是,虽然更直观地理解,但我担心这个版本的性能会降低,所有这些列表都会创建。另一个缺点是“d1”在表达式中出现两次,这是相当容易出错的。可能有一个更好的实现使用get,但目前它没有我。
我的猜测是版本2虽然对于没有经验的人来说更难掌握,但是更快,因此更可取。意见?
答案 0 :(得分:22)
您的两个示例执行相同的操作,但这并不意味着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
执行 less 。您可以使用它来避免在密钥不存在时(如果那是经常发生的事情)获得KeyError
,即使您不想设置密钥。
有关这两种方法的更多信息,请参阅Use cases for the 'setdefault' dict method和dict.get() method returns a pointer。
关于setdefault
的帖子得出的结论是,大部分时间,您都希望使用defaultdict
。关于get
的线程得出的结论是它很慢,并且通常你会更好(快速)做双重查找,使用defaultdict或处理错误(取决于字典的大小和你的用例) )。
答案 1 :(得分:14)
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
不合适的偶然地方的选项。避免使用两种原始版本。
答案 2 :(得分:10)
您可能希望查看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)
还有更多here。
答案 3 :(得分:3)
1。这里用一个很好的例子来解释:
http://code.activestate.com/recipes/66516-add-an-entry-to-a-dictionary-unless-the-entry-is-a/
dict。 setdefault 典型用法
somedict.setdefault(somekey,[]).append(somevalue)
dict。获取典型用法
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])
如果key在字典中,则返回key的值,否则返回default。如果 默认情况下没有给出,默认为None,所以这个方法永远不会 提出了一个KeyError。
setdefault(key[, default])
如果key在字典中,则返回其值。如果没有,插入密钥的值为default并返回默认值。默认默认为无。
答案 4 :(得分: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'}
答案 5 :(得分:1)
对于那些仍在努力理解这两个术语的人,让我告诉您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])
花费288 ns
dic = {}
%%timeit -n 10000 -r 4
fn2(dic, 'A', [1,2,3])
花费128 s
因此,这两种方法之间的时间差异很大。
答案 6 :(得分: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()不会。有关更多信息,请转到here !
答案 7 :(得分:0)
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
,这使得这更容易。要创建一个,您可以传递一个类型或函数来为dict中的每个插槽生成默认值:
from collections import defualtdict
group_by_leading_letter = defaultdict(list)
for level in levels:
group_by_leading_letter[level[0]].append(level)