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 中传递引用。

这对于 strinttuple 等不可变对象无关紧要,因为您无法更改它们,只能将名称指向不同的对象,但对于可变对象如listsetdict。您需要习惯这一点并始终牢记这一点。

编辑: 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,列表方法

即使键存在,Python dict.get(k) 也不返回

为啥 dict.get(key) 而不是 dict[key]?

python字典中dict.get()和dict.setdefault()的异同点

Python字典包含了以下内置方法

Python3基础 dict get 在查询不存在的键时,返回指定的内容