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

2024-11-21 08:33:00
admin
原创
4
摘要:问题描述:我遇到了一种dict方法get,它根据字典中的键返回相关值。这个函数有什么用呢?如果我想在字典中查找与键关联的值,我只需执行dict[key],它就会返回相同的结果:dictionary = {"Name": "Harry", "Age":...

问题描述:

我遇到了一种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/exceptif语句中,但对字典术语的过多关注很快就会堆积起来。

确实,在不常见的情况下,对于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)提供了替代方法(分别是atfetch)来选择在访问不正确时抛出错误,而 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`maxminget`

>>> 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.gettype(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:,当您习惯它时,会使代码更干净,更强大。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用