繁体   English   中英

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

[英]Why dict.get(key) instead of dict[key]?

今天,我遇到了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_valueNone ,这样

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说明特定键不可用时。 返回默认值也会带来特定默认值也可能是键的风险!

是否有可能在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'

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

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

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

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

["

{"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"}}

使用.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之间的区别,并提到了一个相当无害的陷阱,当None或默认值也是有效键时。

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

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

请注意尝试引用不存在的术语如何导致KeyError 这可能会让人头疼,尤其是在处理不可预测的业务数据时。

虽然我们可以将我们的语句包装在try / exceptif语句中,但对字典术语的这种关注很快就会堆积起来。

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

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

程序员之间的一个常见错误是认为异常会引起头痛并试图抑制它们,使用诸如将代码包装在try ... except: pass blocks中的技术。 他们后来意识到,真正令人头疼的事情是永远不会在故障点看到应用程序逻辑被破坏并部署损坏的应用程序。 更好的编程实践是包含所有程序不变量的断言,例如必须在字典中的键。

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

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

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

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

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

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

更喜欢括号查找的其他支持原因(偶尔,放置良好的.get预期默认值):

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

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

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

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

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

0. 总结

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'

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

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

感谢提供此用例的另一个问题的答案

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

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

简短的回答

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

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

基本方法和辅助方法

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

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

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

根据用法应该使用这个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'

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM