Python 2.5中增加的collections.defaultdict极大地减少了对dict的setdefault方法的需求。这个问题是为了我们的集体教育:
在今天的Python 2.6/2.7中,setdefault在哪些方面仍然有用? setdefault的哪些流行用例被collections.defaultdict所取代?
Python 2.5中增加的collections.defaultdict极大地减少了对dict的setdefault方法的需求。这个问题是为了我们的集体教育:
在今天的Python 2.6/2.7中,setdefault在哪些方面仍然有用? setdefault的哪些流行用例被collections.defaultdict所取代?
当前回答
你可以说defaultdict用于在填充dict之前设置默认值,而setdefault用于在填充dict期间或之后设置默认值。
可能是最常见的用例:对项进行分组(在未排序的数据中,否则使用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-ish——有些是可选的,但你想要它们的默认值:
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 )
其他回答
你可以说defaultdict用于在填充dict之前设置默认值,而setdefault用于在填充dict期间或之后设置默认值。
可能是最常见的用例:对项进行分组(在未排序的数据中,否则使用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-ish——有些是可选的,但你想要它们的默认值:
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 )
我经常使用setdefault,在字典中设置默认值(!!)通常是os。环境字典:
# Set the venv dir if it isn't already overridden:
os.environ.setdefault('VENV_DIR', '/my/default/path')
简单点说,是这样的:
# Set the venv dir if it isn't already overridden:
if 'VENV_DIR' not in os.environ:
os.environ['VENV_DIR'] = '/my/default/path')
值得注意的是,你也可以使用结果变量:
venv_dir = os.environ.setdefault('VENV_DIR', '/my/default/path')
但与违约之前相比,这已经不那么必要了。
除了上述建议之外,如果您不想修改已经设置的值,setdefault可能会很有用。例如,当你有重复的数字,你想把它们当作一组。在这种情况下,如果遇到已设置的重复键,则不会更新该键的值。您将保留第一次遇到的值。就好像你只迭代/更新重复的键一次。
下面是一个记录排序列表中键/元素索引的代码示例:
nums = [2,2,2,2,2]
d = {}
for idx, num in enumerate(sorted(nums)):
# This will be updated with the value/index of the of the last repeated key
# d[num] = idx # Result (sorted_indices): [4, 4, 4, 4, 4]
# In the case of setdefault, all encountered repeated keys won't update the key.
# However, only the first encountered key's index will be set
d.setdefault(num,idx) # Result (sorted_indices): [0, 0, 0, 0, 0]
sorted_indices = [d[i] for i in nums]
[编辑]大错特错!setdefault总是会触发long_computation,因为Python很急切。
扩展塔特尔的答案。对我来说,最好的用例是缓存机制。而不是:
if x not in memo:
memo[x]=long_computation(x)
return memo[x]
这需要3行和2到3次查找,我很乐意这样写:
return memo.setdefault(x, long_computation(x))
我认为上面没有提到的另一个用例。 有时你会根据对象的id来保存一个缓存字典,其中主实例在缓存中,当缺少缓存时你想设置缓存。
return self.objects_by_id.setdefault(obj.id, obj)
当您总是希望每个不同的id保留一个实例时,无论每次如何获取obj,这都很有用。例如,当对象属性在内存中更新并延迟保存到存储中时。