为什么使用 dict.get(key) 而不是 dict[key]?
- 2024-11-21 08:33:00
- admin 原创
- 4
问题描述:
我遇到了一种dict
方法get
,它根据字典中的键返回相关值。
这个函数有什么用呢?如果我想在字典中查找与键关联的值,我只需执行dict[key]
,它就会返回相同的结果:
dictionary = {"Name": "Harry", "Age": 17}
dictionary["Name"] == dictionary.get("Name") # True
另请参阅: 如果字典键不可用,则返回默认值
解决方案 1:
如果缺少键,它允许您提供默认值:
dictionary.get("bogus", default_value)
返回default_value
(无论您选择什么),而
dictionary["bogus"]
会引发KeyError
。
如果省略,default_value
则为None
,使得
dictionary.get("bogus") # <-- No default specified -- defaults to None
返回None
就像
dictionary.get("bogus", None)
会。
解决方案 2:
方法是什么
dict.get()
?
正如前面提到的,该get
方法包含一个附加参数,用于指示缺失值。来自文档
get(key[, default])
如果 key 在字典中,则返回 key 的值,否则返回默认值。如果没有给出默认值,则默认为 None,因此此方法永远不会引发
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'
解决方案 3:
get
接受第二个可选值。如果指定的键不存在于您的字典中,则将返回此值。
dictionary = {"Name": "Harry", "Age": 17}
dictionary.get('Year', 'No available data')
>> 'No available data'
如果没有给出第二个参数,None
将会被返回。
如果使用索引dictionary['Year']
,则不存在的键将引发KeyError
。
解决方案 4:
使用时要注意.get()
:
如果字典包含调用中使用的键.get()
并且其值为,则即使提供了默认值None
,该.get()
方法也会返回。None
例如,以下返回与预期None
不符:'alt_value'
d = {'key': None}
assert None is d.get('key', 'alt_value')
.get()
仅当提供的键不在字典中时,才会返回的第二个值,而不是在该调用的返回值为时返回的None
。
解决方案 5:
我将给出一个使用 python 抓取网络数据的实际例子,很多时候你会得到没有值的键,在这种情况下,如果你使用 dictionary['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': None} 是为了避免第二级出错。当然,如果你正在抓取数据,你可以在代码中构建更多的容错能力。比如首先指定一个 if 条件
解决方案 6:
目的是如果找不到键,可以给出默认值,这非常有用
dictionary.get("Name",'harry')
解决方案 7:
这个功能有何用途?
一种特殊用法是用字典计数。假设您想要计算给定列表中每个元素出现的次数。常见的方法是创建一个字典,其中键是元素,值是出现次数。
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
解决方案 8:
其他答案已经清楚地解释了字典括号键控之间的区别,.get
并提到了当默认值也是有效键时的一个相当无害的陷阱。None
有了这些信息,我们可能很容易得出这样的结论:它.get
在某种程度上比括号索引更安全、更好,并且应该总是用它来代替括号查找,正如在停止使用方括号符号来获取 Python 中的字典值中所说的那样,即使在常见情况下他们期望查找会成功(即从不引发KeyError
)。
这篇博文的作者认为.get
“保护你的代码”:
请注意,尝试引用不存在的术语会导致
KeyError
。这可能会带来很大的麻烦,尤其是在处理不可预测的业务数据时。虽然我们可以将我们的语句包装在
try
/except
或if
语句中,但对字典术语的过多关注很快就会堆积起来。
确实,在不常见的情况下,对于null ( None
) 合并或以其他方式填充缺失值以处理不可预测的动态数据,明智地部署.get
是一种有用的 Pythonic 简写工具,用于笨拙的if key in dct:
和try
/except
块,这些块仅存在于在键可能作为程序行为规范的一部分丢失时设置默认值。
但是,用 替换所有括号字典查找(包括您断言必须成功的那些)则.get
是另一回事。这种做法有效地将一类有助于揭示错误的运行时错误降级为往往更难识别和调试的无声非法状态场景。
程序员常犯的一个错误是认为异常会引起麻烦,并试图使用诸如将代码包装在try
...except: pass
块中的技术来抑制它们。他们后来意识到,真正的麻烦是永远看不到应用程序逻辑在故障点的破坏,以及部署一个损坏的应用程序。更好的编程实践是接受所有程序不变量的断言,例如字典中必须包含的键。
错误安全的层次大致如下:
错误类别 | 调试相对容易 |
---|---|
编译时错误 | 简单,去生产线解决问题 |
运行时异常 | 中等;控制需要流向错误,这可能是由于无法预料的边缘情况或难以重现的状态(如线程之间的竞争条件),但至少当它发生时,我们会得到一个清晰的错误消息和堆栈跟踪。 |
静默逻辑错误 | 困难;我们甚至可能不知道它的存在,而当我们知道时,由于缺乏局部性和可能存在多个断言违规,追踪导致它的状态可能非常具有挑战性。 |
当编程语言设计者谈论程序安全性时,一个主要目标是通过将运行时错误提升为编译时错误以及将静默逻辑错误提升为运行时异常或(理想情况下)编译时错误来暴露而不是抑制真正的错误。
Python 作为一种解释型语言,其严重依赖运行时异常而非编译器错误。1 + "a"
默认情况下,缺少方法或属性、非法类型操作(如越界或缺少索引或键)都会引发异常。
某些语言(例如 JS、Java、Rust 和 Go)默认使用映射的回退行为(并且在许多情况下,不提供 throw/raise 替代方案),但 Python 以及 C# 等其他语言默认会抛出异常。Perl/PHP 会发出未初始化值警告。
不加区别地应用于.get
所有字典访问,即使是那些预计不会失败并且没有后备措施来处理None
(或使用任何默认值)在代码中失控的访问,几乎会抛弃 Python 针对此类错误的运行时异常安全网,从而抑制或增加潜在错误的间接性。
支持使用括号查找的其他原因(偶尔,.get
在需要默认值的地方使用):
更喜欢使用语言提供的工具编写标准、惯用的代码。出于上述异常安全原因以及因为这是 Python 字典的默认行为,Python 程序员通常(正确地)更喜欢使用括号。
.get
当您期望提供None
与您断言必须成功的查找无法区分的默认值时,总是使用放弃意图的方式。测试的复杂性随着 允许的新的“合法”程序路径而增加
.get
。实际上,每个查找现在都是一个可能成功或失败的分支——必须测试两种情况以建立覆盖范围,即使默认路径实际上无法通过规范访问(具有讽刺意味的是,这会导致对检索到的值的所有未来使用产生额外if val is not None:
或try
所有额外使用;对于根本不应该出现None
的事情来说,这是不必要和令人困惑的)。.get
有点慢。.get
更难输入,阅读起来也更难看(比较一下 Java 附加的语法ArrayList
和原生的 C#Lists
或 C++ 矢量代码)。轻微的。
某些语言(例如 C++ 和 Ruby)提供了替代方法(分别是at
和fetch
)来选择在访问不正确时抛出错误,而 C# 提供了TryGetValue
类似于 Python 的选择回退值get
。
人们可能会认为,由于 JS、Java、Ruby、Go 和 Rust 默认将 fallback 方法.get
嵌入到所有哈希查找中,因此情况不会那么糟糕。事实上,这并不是语言设计者面临的最大问题,而且无抛出访问版本的用例也很多,因此不同语言之间没有达成共识也就不足为奇了。
但正如我所说,Python(以及 C#)通过将断言选项设为默认选项,比这些语言做得更好。不加区别地.get
全面使用断言选项来报告故障点的违反合同行为,会降低安全性和表达能力。
解决方案 9:
为什么使用 dict.get(key) 而不是 dict[key]?
0.摘要
与 相比dict[key]
,dict.get
在查找键时提供了后备值。
1.定义
get(key[, default]) 4. 内置类型 — Python 3.6.4rc1 文档
如果 key 在字典中,则返回 key 的值,否则返回默认值。如果没有给出默认值,则默认为 None,这样此方法就不会引发 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
如果字典中不存在键,则有一个额外的默认值选项来处理异常
解决方案 10:
一个区别,这可能是一个优势,那就是如果我们正在寻找一个不存在的键,我们将得到 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
解决方案 11:
我没有看到提到的另一个用例是作为诸如、和 等key
函数的参数。该方法允许根据键的值返回键。sorted
`maxmin
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
感谢对另一个问题的回答提供了这个用例!
解决方案 12:
简短回答
方括号用于条件KeyError
查找,当缺少键时可能会失败。
该get()
方法用于无条件查找,由于已提供默认值,因此永远不会失败。
基本方法和辅助方法
方括号调用__getitem__
对于字典等映射至关重要的方法。
该get()
方法是基于该功能的一个辅助层。它是常见编码模式的快捷方式:
try:
v = d[k]
except KeyError:
v = default_value
解决方案 13:
另外,dictionary.get 函数允许你指定一个默认值,如果 key 不存在,另一个也不存在。如果 key 不存在,你可以返回 None。语法是:
dictionary.get('key', default)
当您想要数据库更新列(如果给出了)时,这会变得非常方便,否则在一行中使用现有值。
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.save()
解决方案 14:
在 Python 3.8 及更高版本中,字典方法可以在赋值表达式get()
中与海象运算符一起使用,以进一步减少代码::=
if (name := dictonary.get("Name")) is not None
return name
使用[]
Instead ofget()
需要将代码包装在 try/except 块中并进行捕获KeyError
(未显示)。如果没有海象运算符,您将需要另一行代码:
name = dictionary.get("Name")
if (name is not None)
return name
解决方案 15:
这个功能有何用途?
另一个有用的用例get()
是它从字典中导出内置函数。正如其他答案提到的,可以为指定默认值dict.get
,这意味着如果字典中没有键,则可以返回键本身,例如my_dict.get(key, key)
。这意味着我们可以使用dict.get()
非常简洁地替换值。
例如,从字典中dct = {1: 10}
,我们可以创建函数replacer = dct.get
(type(mapper)
返回builtin_function_or_method
)。然后可以映射此函数来替换值。
lst = [0, 1, 2, 3, 4]
new_list = list(map(replacer, lst, lst)) # [0, 10, 2, 3, 4]
事实上,使用 诱导的函数查找值非常快dict.get()
。以下实验表明,通过该函数查找比通过字典查找快 2 倍以上(在 Python 3.9.12 上完成)。
import timeit
setup = "lst = [0,1]*10000; dct = {1: 10}; replacer = dct.get"
t1 = min(timeit.repeat("list(map(replacer, lst, lst))", setup, number=100))
t2 = min(timeit.repeat("[dct[k] if k in dct else k for k in lst]", setup, number=100))
print(t2 / t1) # 2.707056842200316
解决方案 16:
它允许您提供一个默认值,而不是在找不到该值时出现错误。persuedocode 像这样:
class dictionary():
def get(self,key,default):
if self[key] is not found :
return default
else:
return self[key]
解决方案 17:
.get()
为您提供一个“隐式” try: ... except:
,当您习惯它时,会使代码更干净,更强大。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件