python dict:get与setdefau

2024-05-14 17:38:17 发布

您现在位置:Python中文网/ 问答频道 /正文

以下两个短语对我来说似乎是等价的。哪个更好?

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,虽然对于缺乏经验的人来说更难掌握,但是速度更快,因此更可取。意见?


Tags: keyin版本fordataget字典val
3条回答

agf接受的答案并不是将like与like进行比较。之后:

print timeit("d[0] = d.get(0, []) + [1]", "d = {1: []}", number = 10000)

d[0]包含一个包含10000个项的列表,而在之后:

print timeit("d.setdefault(0, []) + [1]", "d = {1: []}", number = 10000)

d[0]只是[]。i、 e.d.setdefault版本从不修改存储在d中的列表。代码实际上应该是:

print timeit("d.setdefault(0, []).append(1)", "d = {1: []}", number = 10000)

而且事实上比错误的setdefault示例更快。

这里的区别实际上是因为当您使用连接进行追加时,每次都会复制整个列表(并且一旦有10000个元素开始变得可测量)。使用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]

到目前为止,variant 1是最慢的,因为它每次都会复制列表,variant 2是第二慢的,variant 3是最快的,但如果您需要2.5以上的Python,它就不起作用,variant 4只是比variant 3慢一点。

如果可以的话,我会说使用variant 3,使用variant 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)

还有更多的here

你的两个例子做了同样的事情,但这并不意味着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

有关这两种方法的更多信息,请参见Use cases for the 'setdefault' dict methoddict.get() method returns a pointer

关于setdefault的线程得出结论,大多数情况下,您希望使用defaultdict。关于get的线程得出结论,它速度很慢,而且通常您最好(从速度上看)执行双重查找、使用defaultdict或处理错误(取决于字典的大小和您的用例)。

相关问题 更多 >

    热门问题