dict' 的 'setdefault' 方法的用例
在Python 2.5中加入了collections.defaultdict
,这大大减少了我们使用dict
的setdefault
方法的需求。这个问题是为了让大家一起学习:
- 在Python 2.6/2.7中,
setdefault
现在还有什么用处呢? - 有哪些常见的
setdefault
用法被collections.defaultdict
取代了呢?
18 个回答
大多数回答提到,setdefault
或 defaultdict
可以让你在字典中找不到某个键时设置一个默认值。不过,我想提醒大家一个小问题,就是关于 setdefault
的使用场景。当 Python 运行 setdefault
时,它会始终计算这个函数的第二个参数,即使这个键在字典里已经存在。例如:
In: d = {1:5, 2:6}
In: d
Out: {1: 5, 2: 6}
In: d.setdefault(2, 0)
Out: 6
In: d.setdefault(2, print('test'))
test
Out: 6
你可以看到,即使数字 2 已经在字典里了,print
还是被执行了。如果你打算把 setdefault
用于一些优化,比如说 memoization
(记忆化),这个问题就特别重要了。如果你把一个递归函数调用作为 setdefault
的第二个参数,你就得不到任何性能上的提升,因为 Python 会一直递归调用这个函数。
既然提到了记忆化,如果你想给一个函数加上记忆化,使用 functools.lru_cache
装饰器会是一个更好的选择。lru_cache
更好地处理递归函数的缓存需求。
我经常使用 setdefault
来处理带有关键字参数的字典,比如在这个函数中:
def notify(self, level, *pargs, **kwargs):
kwargs.setdefault("persist", level >= DANGER)
self.__defcon.set(level, **kwargs)
try:
kwargs.setdefault("name", self.client.player_entity().name)
except pytibia.PlayerEntityNotFound:
pass
return _notify(level, *pargs, **kwargs)
这个方法非常适合在处理那些需要关键字参数的函数时,调整参数的值。
你可以理解为,defaultdict
是在填充字典之前设置默认值的工具,而 setdefault
则是在填充字典时或之后设置默认值的工具。
最常见的用法可能是:对项目进行分组(在数据未排序的情况下,如果数据已经排序,可以使用 itertools.groupby
)。
# really verbose
new = {}
for (key, value) in data:
if key in new:
new[key].append( value )
else:
new[key] = [value]
# easy with setdefault
new = {}
for (key, value) in data:
group = new.setdefault(key, []) # key might exist already
group.append( value )
# even simpler with defaultdict
from collections import defaultdict
new = defaultdict(list)
for (key, value) in data:
new[key].append( value ) # all keys have a default already
有时候,你想确保在创建字典后某些特定的键是存在的。这个时候 defaultdict
就不太适用了,因为它只会在你明确访问某个键时才创建这个键。想象一下,你在处理一些类似 HTTP 的东西,有很多头部信息——其中一些是可选的,但你希望为它们设置默认值:
headers = parse_headers( msg ) # parse the message, get a dict
# now add all the optional headers
for headername, defaultvalue in optional_headers:
headers.setdefault( headername, defaultvalue )