下面两个表达式对我来说似乎是等价的。哪一个更可取?
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组成的列表连接,并将结果放在”key“中)。但是,虽然更直观地理解,但我担心这个版本的性能较差,因为所有这些列表创建。另一个缺点是"d1“在表达式中出现了两次,这很容易出错。也许有一个使用get的更好的实现,但目前我还不知道。
我的猜测是,版本2虽然对于没有经验的人来说更难掌握,但速度更快,因此更可取。意见?
发布于 2011-09-15 06:14:40
您的两个示例做了相同的事情,但这并不意味着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%左右。
与使用setdefault相比,get方法允许您执行的操作更少。您可以使用它来避免在密钥不存在时获取KeyError (如果这是经常发生的事情),即使您不想设置密钥。
有关这两种方法的更多信息,请参阅Use cases for the 'setdefault' dict method和dict.get() method returns a pointer。
关于setdefault的帖子得出的结论是,在大多数情况下,您都希望使用defaultdict。关于get的帖子总结说它很慢,通常您最好(速度明智地)执行双重查找,使用默认字典,或者处理错误(取决于字典的大小和您的用例)。
发布于 2011-09-15 16:03:44
agf的公认答案不是将like与like进行比较。之后:
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就不会工作,而变体4只是比变体3稍微慢一点。
我会说,如果可以的话,可以使用变体3,对于那些不完全适合defaultdict的地方,可以选择变体4。避免你的两个原始变体。
发布于 2018-09-25 20:23:33
对于那些仍然在努力理解这两个术语的人,让我告诉你get()和setdefault()方法之间的基本区别-
场景-1
root = {}
root.setdefault('A', [])
print(root)场景-2
root = {}
root.get('A', [])
print(root)在-1\f25 Scenario-1 \f6中,输出为-1\f25 {'A': []} -1\f6,而在-2\f25 Scenario-2 \f25 {} -2\f6中输出为-1\f25-1\f6
因此,setdefault()在字典中设置缺少的键,而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秒
因此,这两种方法之间存在非常大的时序差异。
https://stackoverflow.com/questions/7423428
复制相似问题