为啥 dict.get(key) 而不是 dict[key]?
Posted
技术标签:
【中文标题】为啥 dict.get(key) 而不是 dict[key]?【英文标题】:Why dict.get(key) instead of dict[key]?为什么 dict.get(key) 而不是 dict[key]? 【发布时间】:2012-06-17 23:26:13 【问题描述】:今天,我遇到了dict
方法get
,它给定字典中的键,返回关联的值。
这个函数有什么用途?如果我想在字典中查找与某个键关联的值,我可以执行 dict[key]
,它会返回相同的内容:
dictionary = "Name": "Harry", "Age": 17
dictionary["Name"]
dictionary.get("Name")
【问题讨论】:
@yosemite_k 我可能在这里遗漏了一些上下文,但dictionary['non-existent key'] or None
应该并且仍然为我提出了KeyError
(直到 v3.6)。你能解释一下你的意思吗?
dictionary.get("Age") not 与写 dictionary["Age"] 或 None 相同
想知道@yosemite_k 得到的所有支持:dictionary["Age"] or None
绝对与dictionary.get("Age")
不同,因为前者提出了KeyError
。他可能将此与dictionary.get("Age") or 18
混淆,后者类似于dictionary.get("Age", 18)
:如果dictionary
没有键"Age"
,则两个语句都返回默认值18
,但前者在@987654335 时也会这样做@值计算为False
。
@yosemite_k 这是不正确的。您混淆了检索到的值的真实性和.get()
在缺少键时使用后备值,即使它们是错误的。请用 +55 删除您的误导性评论(在撰写本文时)——如果您运行代码,您会发现它们根本不等效。 dictionary["Age"] or None
不会处理 KeyError
如果缺少 "Age"
,它只会引发并且 or None
永远不会执行,这与 dictionary.get("Age")
不同。
【参考方案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)
会的。
【讨论】:
这和dictionary.get("bogus") or my_default
一样吗?我已经看到人们在某些情况下使用它,我想知道使用一个而不是另一个(除了可读性)是否有任何优势
@MustafaS: 如果"bogus"
是dictionary
中的一个键并且dictionary.get("bogus")
返回一个在布尔上下文中评估为False 的值(即一个Falsey 值),例如0 或一个空字符串''
,然后dictionary.get("bogus") or my_default
将评估为my_default
而dictionary.get("bogus", my_default)
将返回Falsey 值。所以不,dictionary.get("bogus") or my_default
不等于 dictionary.get("bogus", my_default)
。使用哪个取决于您想要的行为。
@MustafaS:例如,假设x = 'a':0
。然后x.get('a', 'foo')
返回0
但x.get('a') or 'foo'
返回'foo'
。
使用dictionary.get('key')
时的一个可能警告:如果字典中的值是None
,可能会造成混淆。如果不指定返回值(第二个可选参数),则无法验证键是否不存在或其值是否为None
。在这种特定情况下,我会考虑使用try-except-KeyError
。
值得注意的是,指定默认值的表达式在“get”调用中进行评估,因此在每次访问时都会进行评估。一个经典的替代方法(使用 KeyError 处理程序或谓词)是仅在缺少键时才评估默认值。这允许一个闭包/lambda 被创建一次并在任何缺失的键上进行评估。【参考方案2】:
dict.get()
方法是什么?
如前所述,get
方法包含一个指示缺失值的附加参数。 From the documentation
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
是否有任何地方的速度改进?
如上所述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
正如我们所见,查找比 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 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']
中拥有类似get
的功能?
是的!我们需要在 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'
【讨论】:
Playground link with these code 另一个好的测试是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
,所以不需要再写一个新的类了。【参考方案3】:
get
采用第二个可选值。如果您的字典中不存在指定的键,则返回该值。
dictionary = "Name": "Harry", "Age": 17
dictionary.get('Year', 'No available data')
>> 'No available data'
如果不给第二个参数,则返回None
。
如果您使用dictionary['Year']
中的索引,则不存在的键将引发KeyError
。
【讨论】:
【参考方案4】:我将给出一个使用 python 抓取 web 数据的实际示例,很多时候你会得到没有值的键,在这些情况下,如果你使用 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 条件
【讨论】:
一个很好的答案,在其他任何人之前发布,如果您发布了一些代码示例(不过,我 +1),它会得到更多的支持,并且可能会被接受。 @Mawg 我最近有一个用于研究的抓取项目。它基本上是在调用 API 并解析 JSON 文件。我让我的 RA 做。他遇到的关键问题之一是直接调用密钥,而实际上许多密钥都丢失了。我将在上面的文字中发布一个示例。 感谢您处理此问题的多维方面!听起来您甚至可以只做 而不是 'x': None【参考方案5】:使用.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'【参考方案6】:
目的是如果没有找到key可以给一个默认值,非常有用
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
【讨论】:
虽然这是真的,但请记住d = defaultdict(int)
更干净。内循环变为d[fruit] += 1
。再说一次,collections.Counter
可能比defaultdict
版本更好。如果您不想将 Counter
或 defaultdict
转换回 dict
或类似的东西,.get
版本可能仍然有用。【参考方案8】:
为什么是 dict.get(key) 而不是 dict[key]?
0。总结
与dict[key]
相比,dict.get
在查找键时提供了一个备用值。
1。定义
get(key[, default]) 4. Built-in Types — Python 3.6.4rc1 documentation
如果键在字典中,则返回键的值,否则返回默认值。如果未给出默认值,则默认为 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
有一个额外的默认值选项来处理字典中缺少键时的异常
【讨论】:
【参考方案9】:一个可能是优势的区别是,如果我们正在寻找一个不存在的键,我们将得到 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
【讨论】:
【参考方案10】:我没有看到提到的另一个用例是 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
感谢answer 提供此用例的另一个问题!
【讨论】:
【参考方案11】:其他answers已经清楚地解释了dict括号键和.get
和mentioned a fairly innocuous pitfall在None
或默认值也是有效键时的区别。
鉴于此信息,可能很容易得出以下结论:.get
在某种程度上比括号索引更安全和更好,并且应该始终使用而不是括号查找,如 Stop Using Square Bracket Notation to Get a Dictionary's Value in Python 所述,即使在他们期望查找成功(即永远不要提出KeyError
)。
博文的作者认为.get
“保护你的代码”:
请注意尝试引用不存在的术语如何导致
KeyError
。这可能会让人头疼,尤其是在处理不可预测的业务数据时。虽然我们可以将我们的声明包装在
try
/except
或if
声明中,但对字典术语的这种关注很快就会堆积起来。
确实,在 null (None
)-coalescing 或以其他方式填充缺失值以处理不可预测的动态数据的罕见情况下,明智地部署 .get
是一个有用且 Pythonic 的速记工具if key in dct:
和 try
/except
块仅在作为程序行为规范的一部分可能缺少键时设置默认值。
但是,用.get
替换所有 括号字典查找,包括那些您断言必须成功的查找,是另一回事。这种做法有效地降低了runtime errors 的类别,这有助于将错误揭示为更难以识别和调试的静默非法状态场景。
程序员的一个常见错误是认为异常导致令人头疼并尝试使用诸如在try
... except: pass
blocks 中包装代码之类的技术来抑制它们。他们后来意识到真正的头疼的是从来没有在故障点看到违反应用程序逻辑并部署一个损坏的应用程序。更好的编程实践是包含所有程序不变量的断言,例如必须在字典中的键。
错误安全的层次结构大致是:
Error category | Relative ease of debugging |
---|---|
Compile-time error | Easy; go to the line and fix the problem |
Runtime exception | Medium; control needs to flow to the error and it may be due to unanticipated edge cases or hard-to-reproduce state like a race condition between threads, but at least we get a clear error message and stack trace when it does happen. |
Silent logical error | Difficult; we may not even know it exists, and when we do, tracking down state that caused it can be very challenging due to lack of locality and potential for multiple assertion breaches. |
当编程语言设计者谈论程序安全时,一个主要目标是通过将运行时错误提升为编译时错误并将静默逻辑错误提升为运行时异常或(理想情况下)编译时错误来显示而不是抑制真正的错误.
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
is a bit slower。
.get
更难输入,也更难读(比较 Java 的 tacked-on-feel ArrayList
语法与 native-feel C# Lists
或 C++ 矢量代码)。未成年人。
C++ 和 Ruby 等一些语言提供替代方法(分别为 at
和 fetch
)来选择在错误访问时抛出错误,而 C# 提供选择加入后备值 TryGetValue
,类似于 Python 的get
.
由于 JS、Java、Ruby、Go 和 Rust 默认将 .get
的后备方法烘焙到所有哈希查找中,它不会那么糟糕,人们可能会想。诚然,这不是语言设计者面临的最大问题,并且无抛出访问版本有很多用例,因此跨语言没有达成共识也就不足为奇了。
但正如我所说,Python(以及 C#)通过将 assert 选项设为默认值,比这些语言做得更好。如果不加选择地在故障点不加选择地使用.get
来报告合同违规行为,就会失去安全性和表达能力。
【讨论】:
【参考方案12】:它允许您提供一个默认值,而不是在找不到该值时出错。像这样说服代码:
class dictionary():
def get(self,key,default):
if self[key] is not found :
return default
else:
return self[key]
【讨论】:
【参考方案13】:根据使用情况应该使用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'
【讨论】:
以上是关于为啥 dict.get(key) 而不是 dict[key]?的主要内容,如果未能解决你的问题,请参考以下文章