ChatGPT解决这个技术问题 Extra ChatGPT

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

今天,我遇到了 dict 方法 get,它给定字典中的键,返回关联的值。

这个功能有什么用?如果我想在字典中查找与键关联的值,我可以执行 dict[key],它会返回相同的内容:

dictionary = {"Name": "Harry", "Age": 17}
dictionary["Name"]
dictionary.get("Name")

J
Jean-François Corbett

如果缺少键,它允许您提供默认值:

dictionary.get("bogus", default_value)

返回 default_value(无论您选择什么),而

dictionary["bogus"]

会引发 KeyError

如果省略,default_valueNone,这样

dictionary.get("bogus")  # <-- No default specified -- defaults to None

返回 None 就像

dictionary.get("bogus", None)

将。


这是否与 dictionary.get("bogus") or my_default 相同?我已经看到人们在某些情况下使用它,我想知道使用一个而不是另一个(除了可读性)是否有任何优势
@MustafaS:如果 "bogus"dictionary 中的一个键,并且 dictionary.get("bogus") 返回一个在布尔上下文中评估为 False 的值(即 Falsey 值),例如 0 或空字符串 '',则 { 5} 将评估为 my_defaultdictionary.get("bogus", my_default) 将返回 Falsey 值。所以不,dictionary.get("bogus") or my_default 不等于 dictionary.get("bogus", my_default)。使用哪个取决于您想要的行为。
@MustafaS:例如,假设 x = {'a':0}。然后 x.get('a', 'foo') 返回 0x.get('a') or 'foo' 返回 'foo'
使用 dictionary.get('key') 时可能需要注意的一个问题:如果字典中的值为 None,可能会造成混淆。如果不指定返回值(第二个可选参数),则无法验证键是否不存在或其值是否为 None。在这种特定情况下,我会考虑使用 try-except-KeyError
值得注意的是,指定默认值的表达式在“get”调用中进行评估,因此在每次访问时都会进行评估。一个经典的替代方法(使用 KeyError 处理程序或谓词)是仅在缺少键时才评估默认值。这允许一个闭包/lambda 被创建一次并在任何丢失的键上进行评估。
A
Alexander McFarlane

什么是 dict.get() 方法?

如前所述,get 方法包含一个指示缺失值的附加参数。 From the documentation

get(key[, default]) 如果 key 在字典中,则返回 key 的值,否则返回默认值。如果未给出默认值,则默认为无,因此此方法永远不会引发 KeyError。

一个例子可以是

>>> d = {1:2,2:3}
>>> d[1]
2
>>> d.get(1)
2
>>> d.get(3)
>>> repr(d.get(3))
'None'
>>> d.get(3,1)
1

任何地方都有速度改进吗?

here所述,

似乎所有三种方法现在都表现出相似的性能(彼此相差约 10%),或多或少独立于单词列表的属性。

早期的 get 相当慢,但是现在速度几乎可以与返回默认值的额外优势相媲美。但是为了清除我们所有的查询,我们可以在一个相当大的列表上进行测试(注意,测试只包括查找所有有效的键)

def getway(d):
    for i in range(100):
        s = d.get(i)

def lookup(d):
    for i in range(100):
        s = d[i]

现在使用 timeit 计时这两个函数

>>> import timeit
>>> print(timeit.timeit("getway({i:i for i in range(100)})","from __main__ import getway"))
20.2124660015
>>> print(timeit.timeit("lookup({i:i for i in range(100)})","from __main__ import lookup"))
16.16223979

正如我们所见,查找比获取更快,因为没有函数查找。这可以通过 dis 看到

>>> def lookup(d,val):
...     return d[val]
... 
>>> def getway(d,val):
...     return d.get(val)
... 
>>> dis.dis(getway)
  2           0 LOAD_FAST                0 (d)
              3 LOAD_ATTR                0 (get)
              6 LOAD_FAST                1 (val)
              9 CALL_FUNCTION            1
             12 RETURN_VALUE        
>>> dis.dis(lookup)
  2           0 LOAD_FAST                0 (d)
              3 LOAD_FAST                1 (val)
              6 BINARY_SUBSCR       
              7 RETURN_VALUE  

它将在哪里有用?

每当您要在查找字典时提供默认值时,它都会很有用。这减少了

 if key in dic:
      val = dic[key]
 else:
      val = def_val

对于单行,val = dic.get(key,def_val)

它在哪里没有用处?

每当您想返回一个 KeyError 说明特定密钥不可用时。返回默认值也会带来特定默认值也可能是键的风险!

是否有可能在 dict['key'] 中获得类似的功能?

是的!我们需要在 dict 子类中实现 __missing__

一个示例程序可以是

class MyDict(dict):
    def __missing__(self, key):
        return None

一个小示范可以

>>> my_d = MyDict({1:2,2:3})
>>> my_d[1]
2
>>> my_d[3]
>>> repr(my_d[3])
'None'

另一个好的测试是 if k in dict and dict[k]: vs if dict.get(k):。这涵盖了我们需要检查键是否存在以及如果“是” - 什么值?,例如:dict = {1: '', 2: 'some value'}
请记住,无论字典中的值如何,都会评估默认值,因此可以考虑使用 dictionary.get(value) or long_function() 而不是 dictionary.get(value, long_function())
@Kresimir 这两种方法不一样,因为 None 或 False-y 值将默认,而 dictionary.get() 只有在缺少时才会返回默认值。
collections 模块也有 defaultdict,因此无需再编写新类。
z
zigg

get 采用第二个可选值。如果您的字典中不存在指定的键,则将返回此值。

dictionary = {"Name": "Harry", "Age": 17}
dictionary.get('Year', 'No available data')
>> 'No available data'

如果不提供第二个参数,则返回 None

如果您在 dictionary['Year'] 中使用索引,则不存在的键将引发 KeyError


u
user1847

使用 .get() 时需要注意的问题:

如果字典包含调用 .get() 时使用的键且其值为 None,则即使提供了默认值,.get() 方法也将返回 None

例如,以下返回 None,而不是预期的 'alt_value'

d = {'key': None}
assert None is d.get('key', 'alt_value')

.get() 的第二个值仅在提供的键不在字典中时才会返回,而不是在该调用的返回值为 None 时返回。


这个让我:\ 解决这个问题的一种方法是拥有 d.get('key') or 'alt_value',如果你知道它可能是 None
更像是一个getcha'
k
kevin

我将给出一个使用 python 抓取 web 数据的实际示例,很多时候你会得到没有值的键,在这些情况下,如果你使用 dictionary['key'],你会得到错误,而 dictionary.get('key ', 'return_otherwise') 没有问题。

同样,如果您尝试从列表中捕获单个值,我将使用 ''.join(list) 而不是 list[0] 。

希望能帮助到你。

[编辑] 这是一个实际的例子:

假设您正在调用一个 API,该 API 返回一个您需要解析的 JOSN 文件。第一个 JSON 如下所示:

{"bids":{"id":16210506,"submitdate":"2011-10-16 15:53:25","submitdate_f":"10\/16\/2011 at 21:53 CEST","submitdate_f2":"p\u0159ed 2 lety","submitdate_ts":1318794805,"users_id":"2674360","project_id":"1250499"}}

第二个JOSN是这样的:

{"bids":{"id":16210506,"submitdate":"2011-10-16 15:53:25","submitdate_f":"10\/16\/2011 at 21:53 CEST","submitdate_f2":"p\u0159ed 2 lety","users_id":"2674360","project_id":"1250499"}}

请注意,第二个 JSON 缺少“submitdate_ts”键,这在任何数据结构中都很正常。

因此,当您尝试在循环中访问该键的值时,您可以使用以下方式调用它:

for item in API_call:
    submitdate_ts = item["bids"]["submitdate_ts"]

你可以,但它会给你第二个 JSON 行的回溯错误,因为密钥根本不存在。

对此进行编码的适当方式可能如下:

for item in API_call:
    submitdate_ts = item.get("bids", {'x': None}).get("submitdate_ts")

{'x': None} 是为了避免第二级出错。当然,如果您进行抓取,您可以在代码中构建更多的容错能力。就像首先指定一个 if 条件


一个很好的答案,在其他任何人之前发布,如果您发布了一些代码示例(不过,我 +1),它会得到更多的支持,并且可能会被接受
@Mawg 我最近有一个用于研究的抓取项目。它基本上是在调用 API 并解析 JSON 文件。我让我的 RA 做。他遇到的关键问题之一是直接调用密钥,而实际上许多密钥都丢失了。我将在上面的文本中发布一个示例。
感谢您解决这个问题的多维方面!听起来你甚至可以只做 {} 而不是 {'x': None}
S
SiHa

目的是如果没有找到key可以给一个默认值,非常有用

dictionary.get("Name",'harry')

D
Daniel Holmes

这个功能有什么用?

一种特殊的用法是用字典计数。假设您要计算给定列表中每个元素的出现次数。这样做的常用方法是制作一个字典,其中键是元素,值是出现次数。

fruits = ['apple', 'banana', 'peach', 'apple', 'pear']
d = {}
for fruit in fruits:
    if fruit not in d:
        d[fruit] = 0
    d[fruit] += 1

使用 .get() 方法,您可以使这段代码更加紧凑和清晰:

for fruit in fruits:
    d[fruit] = d.get(fruit, 0) + 1

虽然这是真的,但请记住 d = defaultdict(int) 更干净。内循环变为 d[fruit] += 1。再说一次,可能 collections.Counterdefaultdict 版本更好。如果您不想将 Counterdefaultdict 转换回 dict 或类似的东西,.get 版本可能仍然有用。
g
ggorlen

其他 answers 已经清楚地解释了 dict 括号键和 .getmentioned a fairly innocuous pitfallNone 或默认值也是有效键时的区别。

鉴于此信息,可能很容易得出结论,即 .get 在某种程度上比括号索引更安全和更好,并且应该始终使用而不是括号查找,如 Stop Using Square Bracket Notation to Get a Dictionary's Value in Python 中所述,即使在他们期望查找成功的常见情况下也是如此(即从不提出KeyError)。

博客文章的作者认为 .get “保护您的代码”:

请注意尝试引用不存在的术语如何导致 KeyError。这可能会让人头疼,尤其是在处理不可预测的业务数据时。虽然我们可以将我们的语句包装在 try/except 或 if 语句中,但对字典术语的这种关注很快就会堆积起来。

确实,在 null (None) 的不常见情况下 - 合并或以其他方式填充缺失值以处理不可预测的动态数据,明智地部署 .get 是一个有用且 Python 式的速记工具,用于处理笨拙的 if key in dct:try/except 块,仅当作为程序行为规范的一部分可能缺少键时设置默认值。

但是,用 .get 替换 all 括号字典查找,包括那些您断言必须成功的查找是另一回事。这种做法有效地将有助于将错误揭示为更难识别和调试的静默非法状态场景的 runtime errors 类降级。

程序员的一个常见错误是认为异常导致令人头疼,并尝试使用诸如在 try ... except: pass blocks 中包装代码之类的技术来抑制它们。他们后来意识到真正的头疼的是从来没有在故障点看到违反应用程序逻辑并部署一个损坏的应用程序。更好的编程实践是包含所有程序不变量的断言,例如必须在字典中的键。

错误安全的层次结构大致是:

错误类别 调试相对容易 编译时错误 Easy;去行修复问题 Runtime exception 中;控制需要流向错误,这可能是由于意外的边缘情况或难以重现的状态(如线程之间的竞争条件),但至少当它发生时我们会得到明确的错误消息和堆栈跟踪。沉默的逻辑错误我们甚至可能不知道它的存在,当我们这样做时,由于缺乏局部性和潜在的多个断言违规,追踪导致它的状态可能非常具有挑战性。

当编程语言设计者谈论程序安全时,一个主要目标是通过将运行时错误提升为编译时错误并将静默逻辑错误提升为运行时异常或(理想情况下)编译时错误来显示而不是抑制真正的错误。

Python 在设计上是一种解释性语言,它严重依赖运行时异常而不是编译器错误。默认情况下,缺少方法或属性、非法类型操作(如 1 + "a")和超出范围或缺少索引或键。

某些语言,如 JS、Java、Rust 和 Go,默认情况下使用其映射的回退行为(在许多情况下,不提供 throw/raise 替代方案),但 Python 和其他语言(如 C#)默认情况下会抛出。 Perl/PHP 发出未初始化值警告。

.get 不加选择地应用于所有 dict 访问,即使是那些预计不会失败并且没有回退来处理 None (或使用任何默认值)在代码中运行异常的访问,几乎抛弃了 Python 的运行时异常此类错误的安全网,消除潜在错误或增加对潜在错误的间接性。

更喜欢括号查找的其他支持原因(偶尔使用位置合适的 .get ,其中需要默认值):

更喜欢使用该语言提供的工具编写标准的惯用代码。由于上面给出的异常安全原因,Python 程序员通常(正确地)更喜欢括号,因为它是 Python dicts 的默认行为。

当您希望提供与您断言必须成功的查找无法区分的默认 None 值时,始终使用 .get 放弃意图。

测试的复杂性与 .get 允许的新“合法”程序路径成正比。实际上,每个查找现在都是一个可以成功或失败的分支——这两种情况都必须经过测试以建立覆盖范围,即使默认路径实际上无法通过规范到达(具有讽刺意味的是,如果 val 不是 None: 或尝试所有未来检索到的值的用途;对于本来不应该是 None 的东西来说是不必要的和令人困惑的)。

.get 有点慢。

.get 更难输入,也更难读(将 Java 附加的 ArrayList 语法与原生 C# 列表或 C++ 矢量代码进行比较)。次要的。

某些语言(如 C++ 和 Ruby)提供替代方法(分别为 atfetch)来选择在错误访问时抛出错误,而 C# 提供类似于 Python 的 get 的选择后备值 TryGetValue .

由于默认情况下 JS、Java、Ruby、Go 和 Rust 将 .get 的后备方法烘焙到所有哈希查找中,它不会那么糟糕,人们可能会想。诚然,这不是语言设计者面临的最大问题,并且无抛出访问版本有很多用例,因此跨语言没有达成共识也就不足为奇了。

但正如我所论证的,Python(连同 C#)通过将 assert 选项设为默认值,比这些语言做得更好。不加选择地全面使用 .get 来选择不使用它在故障点报告合同违规行为是一种安全性和表达能力的损失。


A
AbstProcDo

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

0. 总结

dict[key] 相比,dict.get 在查找键时提供备用值。

一、定义

获取(键[,默认值])4. Built-in Types — Python 3.6.4rc1 documentation

如果键在字典中,则返回键的值,否则返回默认值。如果未给出默认值,则默认为无,因此此方法永远不会引发 KeyError。

d = {"Name": "Harry", "Age": 17}
In [4]: d['gender']
KeyError: 'gender'
In [5]: d.get('gender', 'Not specified, please add it')
Out[5]: 'Not specified, please add it'

2.它解决的问题。

如果没有 default value,则必须编写繁琐的代码来处理此类异常。

def get_harry_info(key):
    try:
        return "{}".format(d[key])
    except KeyError:
        return 'Not specified, please add it'
In [9]: get_harry_info('Name')
Out[9]: 'Harry'
In [10]: get_harry_info('Gender')
Out[10]: 'Not specified, please add it'

作为一种方便的解决方案,dict.get 引入了一个可选的默认值,避免了上述笨拙的代码。

三、结论

dict.get 有一个额外的默认值选项来处理字典中缺少键时的异常


א
אנונימי

一个可能是优势的区别是,如果我们正在寻找一个不存在的键,我们将得到 None,不像我们使用方括号表示法时那样,在这种情况下我们会抛出一个错误:

print(dictionary.get("address")) # None
print(dictionary["address"]) # throws KeyError: 'address'

get 方法的最后一件很酷的事情是,它接收一个额外的默认值的可选参数,也就是说,如果我们试图获取学生的分数值,但学生没有我们可以获得的分数键一个 0 代替。

因此,不要这样做(或类似的事情):

score = None
try:
    score = dictionary["score"]
except KeyError:
    score = 0

我们做得到:

score = dictionary.get("score", 0)
# score = 0

d
dshanahan

我没有看到提到的另一个用例是作为 sortedmaxmin 等函数的 key 参数。 get 方法允许根据键的值返回键。

>>> ages = {"Harry": 17, "Lucy": 16, "Charlie": 18}
>>> print(sorted(ages, key=ages.get))
['Lucy', 'Harry', 'Charlie']
>>> print(max(ages, key=ages.get))
Charlie
>>> print(min(ages, key=ages.get))
Lucy

感谢这个answer提供这个用例的另一个问题!


B
Boyce Cecil

它允许您提供默认值,而不是在找不到该值时出错。像这样说服代码:

class dictionary():
    def get(self,key,default):
         if self[key] is not found : 
               return default
         else:
               return self[key]

我知道这是伪代码,但 if self[key] is not found : 会崩溃,所以你不妨把它写成普通的 Python,这样它就不那么混乱了。
R
Raymond Hettinger

简短的回答

方括号用于 条件 查找,当缺少键时,可能会因 KeyError 而失败。

get() 方法用于 unconditional 查找,该查找永远不会失败,因为提供了默认值。

基本方法和辅助方法

方括号调用 __getitem__ 方法,这是 dicts 等映射的基础。

get() 方法是在该功能之上分层的帮助程序。它是通用编码模式的捷径:

try:
    v = d[k]
except KeyError:
    v = default_value  

D
Delimitry

根据使用情况应使用此get 方法。

示例 1

In [14]: user_dict = {'type': False}

In [15]: user_dict.get('type', '')

Out[15]: False

In [16]: user_dict.get('type') or ''

Out[16]: ''

示例 2

In [17]: user_dict = {'type': "lead"}

In [18]: user_dict.get('type') or ''

Out[18]: 'lead'

In [19]: user_dict.get('type', '')

Out[19]: 'lead'

user_dict.get('type') or '' 确实令人困惑,与 get 几乎没有关系,而与 or 完全有关。很难推断一层合并,更不用说两层了。在这些示例中,get 都没有经过实际测试,如果密钥不在字典中,就会发生这种情况。因此,get 的行为与此处所示的 [] 相同。