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)

结果是一样的,但是哪个版本更好或者更像 Python?

就我个人而言,我发现版本2很难理解,因为对我来说 setdefault 非常难以掌握。如果我理解正确,它将在字典中查找“ key”的值(如果不可用) ,在 dict 中输入“[]”,返回对该值或“[]”的引用,并将“ val”附加到该引用。虽然它肯定是光滑的,但至少不是直观的(至少对我来说)。

在我看来,版本1更容易理解(如果可用,获取“ key”的值,如果不可用,获取“[]”,然后加入由[ val ]组成的列表,并将结果放在“ key”中)。但是,虽然更直观的理解,我担心这个版本的性能较差,所有这些列表创建。另一个缺点是“ d1”在表达式中出现两次,这相当容易出错。可能有一个更好的使用 get 的实现,但是目前我还没有找到它。

我的猜测是,版本2,虽然更难掌握的经验不足,更快,因此更好。意见?

57226 次浏览

您可能需要查看 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)

还有更多的 给你

你的两个例子做同样的事情,但这并不意味着 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 方法的用例Get ()方法返回一个指针

关于 setdefault的线程得出的结论是,在大多数情况下,您希望使用 defaultdict。关于 get的线程得出的结论是它很慢,通常最好(在速度方面)进行双重查找,使用默认查找,或者处理错误(取决于字典的大小和用例)。

来自 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是最快的,但是如果你需要的 Python 年龄超过2.5,变体4只是稍微慢于变体3。

如果可以的话,我建议使用变体3,对于那些 defaultdict不完全匹配的地方,变体4可以作为一个选项。避免使用这两种原始变体。

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)


更多解释: http://python.net/~ goodger/Projects/pycon/2007/idiomatic/handout.html”rel = “ norefrer”> http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html

dict.setdefault()相当于 getset & get。或者 set if necessary then get。如果字典键的计算代价很高或者键入时间很长,那么这种方法特别有效。

Setdefault ()的唯一问题是总是计算默认值,无论是否需要。如果默认值为 计算起来很昂贵,则仅为 事宜。在这种情况下,使用默认设置。


最后,有差异的官方文档突出显示了 http://docs.python.org/2/library/stdtypes.html

get(key[, default])
如果 key 在字典中,则返回 key 的值,否则为缺省值 Default 不给定,它默认为 Nothing,所以这个方法永远不会 引发 KeyError。

setdefault(key[, default])
如果 key 在字典中,则返回它的值。如果不是,则 插入钥匙的值为 default 并返回 default。默认设置为无。

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

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)

对于那些仍在努力理解这两个术语的人,让我告诉你 get ()和 setdefault ()方法-之间的基本区别

场景一

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

场景2

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

在场景1中,输出为 {'A': []},而在场景2中为 {}

因此,setdefault()在 dict1中设置缺省键,而 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枪

所以这两种方法在时间上有很大的差异。

这个问题没有严格的答案。他们的目的是一样的。它们都可用于处理键上缺少的值。我发现的唯一不同之处是,对于 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 ()不会。欲了解更多信息,请去 给你