今天,我遇到了一个dict
方法get
,它给定字典中的键,返回关联的值。
这个功能有什么用?如果我想在字典中找到与键关联的值,我可以这样做dict[key]
,它会返回相同的内容:
dictionary = {"Name": "Harry", "Age": 17}
dictionary["Name"]
dictionary.get("Name")
今天,我遇到了一个dict
方法get
,它给定字典中的键,返回关联的值。
这个功能有什么用?如果我想在字典中找到与键关联的值,我可以这样做dict[key]
,它会返回相同的内容:
dictionary = {"Name": "Harry", "Age": 17}
dictionary["Name"]
dictionary.get("Name")
如果缺少键,它允许您提供默认值:
dictionary.get("bogus", default_value)
返回default_value
(无论您选择什么),而
dictionary["bogus"]
会提出一个KeyError
.
如果省略, default_value
is None
, 这样
dictionary.get("bogus") # <-- No default specified -- defaults to None
返回None
就像
dictionary.get("bogus", None)
将。
方法是什么
dict.get()
?
如前所述,该get
方法包含一个指示缺失值的附加参数。从文档
get(key[, default])
如果键在字典中,则返回键的值,否则返回默认值。如果未给出默认值,则默认为无,因此此方法永远不会引发
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
正如我们所见,查找比获取更快,因为没有函数查找。这可以通过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
说明特定密钥不可用的声明时。返回默认值也会带来特定默认值也可能是键的风险!
是否有可能具有
get
类似的功能dict['key']
?
是的!我们需要__missing__
在 dict 子类中实现。
一个示例程序可以是
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'
get
接受第二个可选值。如果您的字典中不存在指定的键,则将返回此值。
dictionary = {"Name": "Harry", "Age": 17}
dictionary.get('Year', 'No available data')
>> 'No available data'
如果不给第二个参数,None
会返回。
如果您使用 in 中的索引dictionary['Year']
,则将引发不存在的键KeyError
。
我将给出一个使用 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 条件
使用时要注意的问题.get()
:
如果字典包含调用中使用的键.get()
并且其值为,则即使提供了默认值None
,该.get()
方法也将返回。None
例如,以下返回None
,'alt_value'
与预期不同:
d = {'key': None}
assert None is d.get('key', 'alt_value')
.get()
的第二个值仅在提供的键不在字典中时才返回,而不是在该调用的返回值为 时None
。
目的是如果没有找到key可以给一个默认值,非常有用
dictionary.get("Name",'harry')
这个功能有什么用?
一种特殊的用法是用字典计数。假设您要计算给定列表中每个元素的出现次数。这样做的常用方法是制作一个字典,其中键是元素,值是出现次数。
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
为什么 dict.get(key) 而不是 dict[key]?
与 相比dict[key]
,dict.get
在查找键时提供备用值。
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'
如果没有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
我没有看到提到的另一个用例是作为key
函数的参数,如sorted
,max
和min
. 该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
感谢提供此用例的另一个问题的答案!
其他答案已经清楚地解释了字典括号键控之间的区别,.get
并提到了一个相当无害的陷阱,当None
或默认值也是有效键时。
鉴于此信息,可能很容易得出结论,即.get
在某种程度上比括号索引更安全和更好,并且应该始终使用而不是括号查找,如Stop Using Square Bracket Notation to Get a Dictionary's Value in Python中所述,即使在常见情况下他们希望查找成功(即从不提高 a KeyError
)。
博客文章的作者认为.get
“保护您的代码”:
请注意尝试引用不存在的术语如何导致
KeyError
. 这可能会让人头疼,尤其是在处理不可预测的业务数据时。虽然我们可以将我们的语句包装在
try
/except
或if
语句中,但对字典术语的这种关注很快就会堆积起来。
确实,在null ( None
) 合并或以其他方式填充缺失值以处理不可预测的动态数据的罕见情况下,明智地部署.get
是一种有用且 Pythonic 的速记工具,用于仅在以下情况下设置默认值的笨拙if key in dct:
和try
/块except
作为程序行为规范的一部分,密钥可能会丢失。
但是,替换所有括号 dict 查找,包括您断言必须成功的那些,.get
是另一回事。这种做法有效地将一类运行时错误降级,这些错误有助于将错误揭示为更难以识别和调试的静默非法状态场景。
程序员的一个常见错误是认为异常会引起头痛并试图抑制它们,使用诸如将代码包装在try
...except: pass
块中的技术。他们后来意识到,真正令人头疼的事情是永远不会在故障点看到应用程序逻辑被破坏并部署损坏的应用程序。更好的编程实践是包含所有程序不变量的断言,例如必须在字典中的键。
错误安全的层次结构大致是:
错误类别 | 调试相对容易 |
---|---|
编译时错误 | 简单的; 去生产线解决问题 |
运行时异常 | 中等的; 控制需要流向错误,这可能是由于意外的边缘情况或难以重现的状态(如线程之间的竞争条件),但至少当它发生时我们会得到明确的错误消息和堆栈跟踪。 |
沉默的逻辑错误 | 难的; 我们甚至可能不知道它的存在,当我们这样做时,由于缺乏局部性和多个断言违规的可能性,追踪导致它的状态可能非常具有挑战性。 |
当编程语言设计者谈论程序安全时,一个主要目标是通过将运行时错误提升为编译时错误并将静默逻辑错误提升为运行时异常或(理想情况下)编译时错误来显示而不是抑制真正的错误。
Python 在设计上是一种解释性语言,它严重依赖运行时异常而不是编译器错误。默认情况下,缺少方法或属性、非法类型操作(如1 + "a"
越界或缺少索引或键)。
一些语言,如 JS、Java、Rust 和 Go,默认情况下使用其映射的后备行为(在许多情况下,不提供 throw/raise 替代方案),但 Python 和其他语言(如 C#)默认抛出。Perl/PHP 发出未初始化值警告。
不加选择地应用.get
到所有 dict 访问,即使是那些预计不会失败并且没有后备来处理None
(或使用任何默认值)在代码中乱跑的人,这几乎抛弃了 Python 的此类运行时异常安全网错误,沉默或增加对潜在错误的间接性。
更喜欢括号查找的其他支持原因(偶尔会.get
在需要默认值的地方很好地放置):
.get
当您希望提供一个None
与您断言必须成功的查找无法区分的默认值时,始终使用没收意图。.get
. 实际上,每个查找现在都是一个可以成功或失败的分支——这两种情况都必须经过测试以建立覆盖范围,即使默认路径实际上无法通过规范达到(具有讽刺意味的是,这会导致对检索值的额外使用if val is not None:
或try
所有未来使用;不必要并为那些一开始就不应该出现的事情感到困惑None
)。.get
是有点慢。.get
更难打字,更难读(比较 Java 的 tacked-on-feelArrayList
语法与原生感觉 C#Lists
或 C++ 矢量代码)。次要的。一些语言,如 C++ 和 Ruby 提供替代方法(at
和fetch
,分别)来选择在错误访问时抛出错误,而 C# 提供TryGetValue
类似于 Python 的选择加入后备值get
。
由于 JS、Java、Ruby、Go 和 Rust 在.get
默认情况下将后备方法烘焙到所有哈希查找中,所以它不会那么糟糕,人们可能会想。确实,这不是语言设计者面临的最大问题,并且有很多用于无抛出访问版本的用例,因此跨语言没有达成共识也就不足为奇了。
但正如我所论证的,Python(连同 C#)通过将 assert 选项设为默认值,比这些语言做得更好。选择不使用它在故障点不加选择地全面使用它来报告合同违规行为是一种安全性和表达能力的损失.get
。
它允许您提供默认值,而不是在找不到该值时出错。像这样说服代码:
class dictionary():
def get(self,key,default):
if self[key] is not found :
return default
else:
return self[key]
根据使用情况应该使用这种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'