dict.get() method returns a pointer
假设我有这个代码:
1 2 3 4 5 6 7 8 9 10 11 | my_dict = {} default_value = {'surname': '', 'age': 0} # get info about john, or a default dict item = my_dict.get('john', default_value) # edit the data item[surname] = 'smith' item[age] = 68 my_dict['john'] = item |
如果我们现在检查默认值,问题就变得很明显了:
1 2 | >>> default_value {'age': 68, 'surname': 'smith'} |
很明显,
通过将代码更改为:
1 | item = my_dict.get('john', {'surname': '', 'age': 0}) |
但这似乎不是一个很好的方法。有什么想法,意见吗?
1 | item = my_dict.get('john', default_value.copy()) |
您总是在python中传递一个引用。
对于不可变的对象,如
编辑:ZachBloom和JonathanSternberg都指出了避免每次查找时调用
1 2 3 4 5 | def my_dict_get(key): try: item = my_dict[key] except KeyError: item = default_value.copy() |
如果
看乔纳森的回答,他用一个小的
不要使用GET。你可以这样做:
1 | item = my_dict.get('john', default_value.copy()) |
但这要求即使字典条目存在,也要复制字典。相反,考虑检查值是否存在。
1 | item = my_dict['john'] if 'john' in my_dict else default_value.copy() |
唯一的问题是它将对"john"执行两次查找,而不是一次。如果您愿意使用一个额外的行(并且没有一个是您从字典中得到的可能值),您可以这样做:
1 2 3 | item = my_dict.get('john') if item is None: item = default_value.copy() |
编辑:我想我会和timeit做一些速度比较。默认值和我的口述是全局的。如果钥匙在那里,如果有失手的话,我会为两个人都做。
使用例外:
1 2 3 4 5 6 7 8 | def my_dict_get(): try: item = my_dict['key'] except KeyError: item = default_value.copy() # key present: 0.4179 # key absent: 3.3799 |
使用get并检查它是否为none。
1 2 3 4 5 6 7 | def my_dict_get(): item = my_dict.get('key') if item is None: item = default_value.copy() # key present: 0.57189 # key absent: 0.96691 |
使用特殊的if/else语法检查其存在性
1 2 3 4 5 | def my_dict_get(): item = my_dict['key'] if 'key' in my_dict else default_value.copy() # key present: 0.39721 # key absent: 0.43474 |
天真地抄写字典。
1 2 3 4 5 | def my_dict_get(): item = my_dict.get('key', default_value.copy()) # key present: 0.52303 (this may be lower than it should be as the dictionary I used was one element) # key absent: 0.66045 |
在大多数情况下,除了使用异常的情况外,其他情况都非常相似。由于某种原因,特殊的if/else语法的时间似乎最短(不知道为什么)。
在python中,dict都是对象(因此它们总是作为引用传递)和可变对象(这意味着它们可以在不重新创建的情况下进行更改)。
每次使用词典时,您都可以复制词典:
1 | my_dict.get('john', default_value.copy()) |
还可以使用defaultdict集合:
1 2 3 4 5 6 7 8 | from collections import defaultdict def factory(): return {'surname': '', 'age': 0} my_dict = defaultdict(factory) my_dict['john'] |
要认识到的主要事情是,Python中的所有内容都是通过引用传递的。C样式语言中的变量名通常是内存中对象形状区域的简写,将该变量赋值可复制另一个对象形状区域…在Python中,变量只是字典(
这有许多含义,主要的含义是永远不会有一个对象的隐式副本,因为您将它传递给了一个函数,分配了它,等等。获得副本的唯一方法是显式地这样做。python stdlib提供了一个
在代码的情况下,传递原始实例显然不起作用,并且提前(可能不需要时)制作一个副本是浪费的。所以最简单的解决方案可能是…
1 2 3 | item = my_dict.get('john') if item is None: item = default_dict.copy() |
在这种情况下,如果
因为每次调用get时,
1 2 3 4 | try: return my_dict['john'] except KeyError: return {'surname': '', 'age': 0} |
或者,您也可以使用
1 2 3 4 5 6 | import collections def default_factory(): return {'surname': '', 'age': 0} my_dict = collections.defaultdict(default_factory) |