dict.get() 方法返回一个指针
Posted
技术标签:
【中文标题】dict.get() 方法返回一个指针【英文标题】:dict.get() method returns a pointer 【发布时间】:2011-11-01 12:07:42 【问题描述】:假设我有这个代码:
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
如果我们现在检查 default_value 的值,问题就很清楚了:
>>> default_value
'age': 68, 'surname': 'smith'
很明显,my_dict.get()
并没有返回 default_value 的 value,而是一个指向它的指针 (?)。
可以通过将代码更改为:
item = my_dict.get('john', 'surname': '', 'age': 0)
但这似乎不是一个好方法。有什么想法吗,cmets?
【问题讨论】:
【参考方案1】:因为my_dict.get('john', default_value.copy())
每次 get 被调用时都会创建一个默认字典的副本(即使存在并返回 'john'),使用这个 try/except 更快也很好选项:
try:
return my_dict['john']
except KeyError:
return 'surname': '', 'age': 0
或者,您也可以使用defaultdict
:
import collections
def default_factory():
return 'surname': '', 'age': 0
my_dict = collections.defaultdict(default_factory)
【讨论】:
【参考方案2】:item = my_dict.get('john', default_value.copy())
您总是在 Python 中传递引用。
这对于 str
、int
、tuple
等不可变对象无关紧要,因为您无法更改它们,只能将名称指向不同的对象,但对于可变对象如list
、set
和 dict
。您需要习惯这一点并始终牢记这一点。
编辑: Zach Bloom 和 Jonathan Sternberg 都指出了可以用来避免在每次查找时调用 copy
的方法。您应该使用 defaultdict
方法,类似于 Jonathan 的第一种方法,或者:
def my_dict_get(key):
try:
item = my_dict[key]
except KeyError:
item = default_value.copy()
如果my_dict
中几乎总是存在密钥,这将比if
快,如果dict
很大。您不必将其包装在一个函数中,但您可能不希望每次访问 my_dict
时都需要这四行。
请参阅 Jonathan 的回答,了解带有小 dict
的时间。 get
方法在我测试的所有尺寸上都表现不佳,但try
方法在大尺寸下表现更好。
【讨论】:
这是python的一个非常重要的原则——所有值都是通过引用传递的。这些引用的可变性是一个完全不同的问题(尽管它经常以这种方式绊倒人们)。 我确信我以前读过它,但是当你长时间不使用一种语言时,你往往会忘记一些事情。谢谢你说清楚。 为什么您的答案与问题中提供的答案有任何不同。问题似乎更多是关于找到一种优雅的方式来返回字典的新实例,但只在需要时创建它。 问题是关于每次他需要执行get
时不要输入'surname': '', 'age': 0
,以及如何在Python 中传递参数,而不是每次都创建它。但是,我已经指出了其他提到该问题的答案,并添加了一个替代方案。
@agf 查看我对速度计时的回答。使用 try/except 会比他想要的慢得多。【参考方案3】:
不要使用get。你可以这样做:
item = my_dict.get('john', default_value.copy())
但这需要复制字典即使字典条目存在。相反,请考虑仅检查该值是否存在。
item = my_dict['john'] if 'john' in my_dict else default_value.copy()
唯一的问题是它会为 'john' 执行两次查找,而不仅仅是一次。如果您愿意使用额外的一行(并且 None 不是您可以从字典中获得的可能值),您可以这样做:
item = my_dict.get('john')
if item is None:
item = default_value.copy()
编辑:我想我会与 timeit 进行一些速度比较。 default_value 和 my_dict 是全局变量。如果钥匙在那里,如果有遗漏,我都做了。
使用异常:
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。
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 语法检查它的存在
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
天真地复制字典。
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 语法似乎时间最短(不知道为什么)。
【讨论】:
这是一个很好的观点,我会在我的答案中添加一个注释。用'john' in my_dict
代替my_dict.has_key('john')
和my_dict.get('john')
代替my_dict.get('john', None)
怎么样?
比起has_key,我更喜欢in的使用。我忘记了它的存在。我不知道 my_dict.get('john') 默认返回 null(我以为是 IndexError)。
或者使用这个: from collections import defaultdict mydict = defaultdict(default_value.copy) 然后当你做 mydict[key-thats-not-here] 时,你传递给构造函数的函数会被调用。 【参考方案4】:
要意识到的主要一点是,Python 中的 一切 都是通过引用传递的。 C 风格语言中的变量名通常是对象形状的内存区域的简写,分配给该变量会复制另一个对象形状的区域……在 Python 中,变量只是字典中的键(@ 987654322@),而赋值行为只是存储了一个新的引用。 (从技术上讲,everything 是一个指针,但这是一个实现细节。
这有很多含义,主要的是永远不会有一个对象的隐式副本,因为你将它传递给一个函数,分配它等等。获取副本的唯一方法是显式这样做. python stdlib 提供了一个 copy
模块,其中包含一些东西,包括一个 copy()
和 deepcopy()
函数,用于当您想要显式复制某些内容时。此外,某些类型公开了自己的 .copy()
函数,但这不是标准,也不是一致实现的。其他不可变的方法有时会提供.replace()
方法,该方法会生成变异副本。
就您的代码而言,传入原始实例显然不起作用,并且提前制作副本(当您可能不需要时)是浪费。所以最简单的解决方案可能是......
item = my_dict.get('john')
if item is None:
item = default_dict.copy()
如果.get()
支持传入默认值构造函数,这在这种情况下会很有用,但这可能会为边界情况过度设计基类。
【讨论】:
【参考方案5】:在 Python 中,dict 既是对象(因此它们总是作为引用传递)又是可变的(意味着它们可以在不重新创建的情况下更改)。
您可以在每次使用时复制您的字典:
my_dict.get('john', default_value.copy())
你也可以使用defaultdict集合:
from collections import defaultdict
def factory():
return 'surname': '', 'age': 0
my_dict = defaultdict(factory)
my_dict['john']
【讨论】:
以上是关于dict.get() 方法返回一个指针的主要内容,如果未能解决你的问题,请参考以下文章
为啥 dict.get(key) 而不是 dict[key]?