为什么dict.get(键)而不是字典[键]?

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

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

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

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

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("Name",'harry')

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

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

如果您没有给出第二个参数,将返回None

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

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

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

希望有帮助。

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

假设,您正在调用一个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':无}是为了避免第二层出错。当然,如果你在进行抓取,你可以在代码中构建更多的容错能力。比如首先指定一个if条件

什么是dict.get()方法?

如前所述,get方法包含一个指示缺失值的附加参数。从留档

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

任何地方都有速度提升吗?

如上所述这里

似乎所有这三种方法现在都表现出相似的性能(大约在彼此的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

正如我们所看到的,查找比get更快,因为没有函数查找。这可以通过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            112 RETURN_VALUE>>> dis.dis(lookup)2           0 LOAD_FAST                0 (d)3 LOAD_FAST                1 (val)6 BINARY_SUBSCR7 RETURN_VALUE

它在哪里有用?

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

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

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

哪里会没有用?

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

是否有可能在dict['key']中有get喜欢的功能?

是的!我们需要在一个字典子类中实现__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'

这个功能有什么用?

一个特殊的用法是用字典计数。让我们假设您想计算给定列表中每个元素的出现次数。常见的方法是制作一个字典,其中键是元素,值是出现次数。

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

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

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

为什么dict.get(键)而不是字典[键]?

0.摘要

dict[key]相比,dict.get在查找键时提供了一个回退值。

1.定义

get(key[, default])4.内置类型-Python 3.6.4rc1留档

如果键在字典中,则返回键的值,否则为默认值。如果未给出默认值,则默认为无,因此此方法永远不会引发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引入了一个可选的默认值,避免了上述不方便的代码。

3.结论

dict.get有一个额外的默认值选项来处理字典中没有key的异常

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

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

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

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

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

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

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

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

而不是这样做(或类似的事情):

score = Nonetry:score = dictionary["score"]except KeyError:score = 0

我们可以这样做:

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

另一个我没有看到提到的用例是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

感谢这个回答来提供这个用例的不同问题!

其他答案已经清楚地解释了当None或默认值也是有效键时,字典括号键控与.get提到了一个相当无害的陷阱之间的区别。

鉴于这些信息,可能很容易得出结论,.get在某种程度上比括号索引更安全,更好,并且应该始终使用括号查找,正如停止使用方括号表示法在Python中获取字典的值所述,即使在他们期望查找成功的常见情况下(即永远不会引发KeyError)。

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

请注意,试图引用一个不存在的术语会导致KeyError。这可能会引起很大的麻烦,尤其是在处理不可预测的业务数据时。

虽然我们可以用try/exceptif语句包装我们的语句,但对字典术语的如此关注会很快堆积起来。

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

但是,用.get替换所有括号字典查找,包括那些你断言必须成功的查找,是另一回事。这种做法有效地降级了一类运行时错误,这些运行时错误有助于将错误暴露为沉默的非法状态场景,这些场景往往更难识别和调试。

程序员的一个常见错误是认为异常导致令人头疼,并试图抑制它们,使用像在#0…#1个街区中包装代码这样的技术。他们后来意识到真正令人头疼的是永远不会在故障点看到应用程序逻辑的破坏,并部署一个破碎的应用程序。更好的编程实践是接受所有程序不变量的断言,例如必须在字典中的键。

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

错误类别调试相对容易
编译时错误简单;去线上解决问题
运行时异常中等;控制需要流向错误,这可能是由于意想不到的边缘情况或难以重现的状态,例如线程之间的竞争条件,但至少我们在发生错误时会得到明确的错误消息和堆栈跟踪。
无声逻辑错误困难;我们甚至可能不知道它的存在,当我们这样做时,由于缺乏局部性和多次断言违规的可能性,跟踪导致它的状态可能非常具有挑战性。

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

Python作为一种解释型语言,在很大程度上依赖于运行时异常而不是编译器错误。默认情况下会引发缺少方法或属性、非法类型操作(如1 + "a"和越界)或缺少索引或键。

一些语言,如JS,Java,Rust和Go,默认使用它们的map的回退行为(在许多情况下,不提供抛出/提升替代方案),但Python默认抛出,以及其他语言,如C#。Perl/PHP发出未初始化的值警告。

不分青红皂白地将.get应用于所有字典访问,即使是那些预计不会失败并且没有回退来处理None(或使用任何默认值)在代码中疯狂运行的访问,几乎丢弃了Python针对此类错误的运行时异常安全网,沉默或间接添加潜在错误。

其他支持更喜欢括号查找的原因(偶尔,在默认情况下放置.get):

  • 更喜欢使用语言提供的工具编写标准的惯用代码。Python程序员通常(正确地)更喜欢括号,因为上面给出的异常安全原因以及它是Python字典的默认行为。
  • 当您希望提供与您断言必须成功的查找无法区分的默认None值时,总是使用.get会丧失意图。
  • 测试的复杂性与.get所允许的新的“合法”程序路径成正比。实际上,每个查找现在都是一个可以成功或失败的分支——两种情况都必须进行测试以建立覆盖范围,即使默认路径实际上无法通过规范到达(具有讽刺意味的是,这会导致对检索到的值的所有未来用途增加if val is not None:try;对于一开始就不应该是None的东西来说,这是不必要的和令人困惑的)。
  • #0有点慢.
  • .get更难打字,更难阅读(比较Java的teded-on-的ArrayList语法和原生感觉的C#Lists或C++矢量代码)。

C++和Ruby等一些语言提供了替代方法(分别为atfetch)来选择在错误访问时抛出错误,而C#提供了类似于Pythonget的选择加入回退值TryGetValue

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

但正如我所说,Python(以及C#)通过将assert选项设为默认选项,比这些语言做得更好。通过不分青红皂白地使用.get来选择不使用它来报告故障时的合同违规行为,这是一种安全性和表现力的损失。

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

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

简短的回答

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

get()方法用于无条件查找,这些查找永远不会失败,因为已提供默认值。

基础方法和辅助方法

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

get()方法是该功能之上的帮助器。它是常见编码模式的快捷方式:

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

在Python 3.8及之后,字典get()方法可以与赋值表达式中的海象运算符:=一起使用,以进一步减少代码:

if (name := dictonary.get("Name")) is not Nonereturn name

使用[]而不是get()需要将代码包装在try/除了块中并捕获KeyError(未显示)。如果没有walrus运算符,你将需要另一行代码:

name = dictionary.get("Name")if (name is not None)return name