Use cases for the 'setdefault' dict method
在Python 2.5中添加
您可以说
可能是最常见的用例:对项目进行分组(在未排序的数据中,否则使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 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 new = defaultdict(list) for (key, value) in data: new[key].append( value ) # all keys have a default already |
有时您希望在创建dict后确保存在特定键。
1 2 3 4 | 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 ) |
我通常使用
1 2 3 4 5 6 7 8 | 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) |
在包含关键字参数的函数的包装器中调整参数是很好的。
当默认值是静态时(如新列表),
例如,我需要一个字典来将字符串映射到唯一的int。
相反,我使用了常规字典:
1 2 3 4 5 | nextID = intGen() myDict = {} for lots of complicated stuff: #stuff that generates unpredictable, possibly already seen str strID = myDict.setdefault(myStr, nextID()) |
请注意
1 2 3 4 5 6 7 | class intGen: def __init__(self): self.i = 0 def __call__(self): self.i += 1 return self.i |
如果某人有办法用
当我想在
正如穆罕默德所说,在某些情况下,您有时只希望设置默认值。一个很好的例子是首先填充,然后查询的数据结构。
考虑一个特里。添加单词时,如果需要子节点但不存在,则必须创建子节点以扩展trie。当查询单词的存在时,丢失的子节点表示该单词不存在且不应创建。
defaultdict无法执行此操作。相反,必须使用带有get和setdefault方法的常规字典。
从理论上讲,如果你有时想要设置默认值,有时候不是,那么
但是,一个有趣的用例来自标准库(Python 2.6,_threadinglocal.py):
1 2 3 4 5 6 7 | >>> mydata = local() >>> mydata.__dict__ {'number': 42} >>> mydata.__dict__.setdefault('widgets', []) [] >>> mydata.widgets [] |
我会说使用
编辑:碰巧,这是标准库中唯一的例子,它在评论中。因此,可能仅仅证明
对象将其属性存储在
附:不意味着改变对象的IMO函数方法不应该改变对象。
由于大多数答案状态
1 2 3 4 5 6 7 8 9 10 11 | 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,也会执行
以下是setdefault的一些示例,以显示其用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | """ d = {} # To add a key->value pair, do the following: d.setdefault(key, []).append(value) # To retrieve a list of the values for a key list_of_values = d[key] # To remove a key->value pair is still easy, if # you don't mind leaving empty lists behind when # the last value for a given key is removed: d[key].remove(value) # Despite the empty lists, it's still possible to # test for the existance of values easily: if d.has_key(key) and d[key]: pass # d has some values for key # Note: Each value can exist multiple times! """ e = {} print e e.setdefault('Cars', []).append('Toyota') print e e.setdefault('Motorcycles', []).append('Yamaha') print e e.setdefault('Airplanes', []).append('Boeing') print e e.setdefault('Cars', []).append('Honda') print e e.setdefault('Cars', []).append('BMW') print e e.setdefault('Cars', []).append('Toyota') print e # NOTE: now e['Cars'] == ['Toyota', 'Honda', 'BMW', 'Toyota'] e['Cars'].remove('Toyota') print e # NOTE: it's still true that ('Toyota' in e['Cars']) |
我没有想到的另一个用例如上所述。
有时你通过id来保存对象的缓存字典,其中主要实例在缓存中,并且你想在丢失时设置缓存。
1 | return self.objects_by_id.setdefault(obj.id, obj) |
无论你每次如何获得一个obj,当你总是想要为每个不同的id保留一个实例时,这很有用。例如,当对象属性在内存中更新并且延迟保存到存储时。
我偶然发现了一个非常重要的用例:当你只需要一个规范对象(而不是多个碰巧相同的对象)时,
例如,Python 3.6.0中的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | from enum import IntFlag, auto import threading class TestFlag(IntFlag): one = auto() two = auto() three = auto() four = auto() five = auto() six = auto() seven = auto() eight = auto() def __eq__(self, other): return self is other def __hash__(self): return hash(self.value) seen = set() class cycle_enum(threading.Thread): def run(self): for i in range(256): seen.add(TestFlag(i)) threads = [] for i in range(8): threads.append(cycle_enum()) for t in threads: t.start() for t in threads: t.join() len(seen) # 272 (should be 256) |
解决方案是使用
我经常使用setdefault,得到这个,在字典中设置默认值(!!!); os.environ字典有点普遍:
1 2 | # Set the venv dir if it isn't already overridden: os.environ.setdefault('VENV_DIR', '/my/default/path') |
不太简洁,这看起来像这样:
1 2 3 | # Set the venv dir if it isn't already overridden: if 'VENV_DIR' not in os.environ: os.environ['VENV_DIR'] = '/my/default/path') |
值得注意的是,您还可以使用结果变量:
1 | venv_dir = os.environ.setdefault('VENV_DIR', '/my/default/path') |
但这比默认存在之前的那些要少。
[编辑]非常错! setdefault总是会触发long_computation,Python很渴望。
扩展塔特尔的答案。对我来说,最好的用例是缓存机制。代替:
1 2 3 | if x not in memo: memo[x]=long_computation(x) return memo[x] |
消耗3行和2或3次查找,我很乐意写 strike>:
1 | return memo.setdefault(x, long_computation(x)) |
我喜欢这里给出的答案:
http://stupidpythonideas.blogspot.com/2013/08/defaultdict-vs-setdefault.html
简而言之,应该根据您希望如何处理下游空键的查找(即
1 2 3 4 5 6 | >>> from collection import defaultdict() >>> foo = defaultdict() >>> foo['a'] = 4 >>> foo['a'] = 2 >>> print(foo) defaultdict(None, {'a': 2}) |
1 2 3 4 5 | >>> bar = dict() >>> bar.setdefault('a', 4) >>> bar.setdefault('a', 2) >>> print(bar) {'a': 4} |
我重写了接受的答案,并为新手提供了便利。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | #break it down and understand it intuitively. new = {} for (key, value) in data: if key not in new: new[key] = [] # this is core of setdefault equals to new.setdefault(key, []) new[key].append(value) else: new[key].append(value) # easy with setdefault new = {} for (key, value) in data: group = new.setdefault(key, []) # it is new[key] = [] group.append(value) # even simpler with defaultdict new = defaultdict(list) for (key, value) in data: new[key].append(value) # all keys have a default value of empty list [] |
另外,我将这些方法分类为参考:
1 2 3 4 5 6 | dict_methods_11 = { 'views':['keys', 'values', 'items'], 'add':['update','setdefault'], 'remove':['pop', 'popitem','clear'], 'retrieve':['get',], 'copy':['copy','fromkeys'],} |