为什么 Python 在访问未设置的 属性 时抛出 KeyError?
Why does Python throw a KeyError when accessing an unset property?
在 Python 字典上访问未设置的 属性 时抛出 KeyError
而不是返回 None
的基本原理和含义是什么?
我知道语言设计涉及权衡,其中一些基于实际问题,一些主要基于哲学。从计算机科学的角度来看,为什么 Python 是这样设计的?
提出问题的几个注释
- 我主要想知道这是否是一个实际的决定(即,它为开发人员提供了更多从错误中恢复的选择,而不仅仅是返回
None
),或者这个选择是否具有哲学意义(即, "accessing an unset property is, in Guido's opinion, an exceptional case").
- Python 的
dict.get
函数具有此功能,但文字符号似乎针对程序认为未设置值异常的情况进行了优化。
- 值得注意的是,异常处理被一些人认为是难以推理的,因为它涉及 a control flow that is distinct from the main program
- 如果 Python 返回
None
而不是引发异常,程序仍然可以在访问其值之前检查键是否存在,如果它确实想要处理未设置和 None
价值观不同。但是由于我们无法访问异常编程流程,我很想知道我们可能会失去什么?
首先,让我们回顾一下我们的术语:
...when accessing an unset property on a Python dictionary
您没有访问 属性。您正在检索密钥。这就是为什么它被称为 KeyError
。属性是属性的特例,这就是为什么它们会引发 AttributeError
而不是
至于为什么,还要感谢类型论。我有一本字典 d
,其中(比如说)字符串作为键,整数作为值。对于 any k
,这条规则确实应该成立:
assert isinstance(d[k], int)
如果您 return None
,那么在这种情况下规则将失败。引发异常是表明您无法满足操作的后置条件的正常方式。在这种情况下,断言没有机会触发,因为在表达式仍在计算时引发了异常。
- Errors should not be fatal. That is, user code should be able to recover from error conditions as long as the virtual machine is still functional.
- At the same time, errors should not pass silently (These last two items naturally led to the decision to use exceptions throughout the implementation.)
还有一个经典案例,不喜欢大喊大叫的语言被破坏了:None
并不总是意味着缺少密钥(这意味着消息被破坏),它也可能意味着可选的,但未设置密钥(没关系)。以JSON:
为例
>>> j = json.loads('{ "key": null }')
>>> j['key']
>>> j['nokey']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'nokey'
使用 None
-默认情况下,此示例变得更加笨拙。以 PHP 为例:
json_decode
returns null for invalid input, even though null is also a perfectly valid object for JSON to decode to—this function is completely unreliable unless you also call json_last_error
every time you use it.
一个dict
可以存储任何值(包括None
)所以唯一的方法(当使用[]
语法时)来区分在丢失的密钥和映射到您认为的任何内容的密钥之间
将是一个很好的哨兵是提出一个例外。
>>> d = {'foo': None}
>>> print d['foo']
None
>>> print d['bar']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'bar'
对于您不关心区别的情况,您可以改用 get
方法,它故意 returns None
(或您选择的标记)用于不存在的键。
>>> print d.get('bar')
None
>>> print d.get('bar', 7)
7
在 Python 字典上访问未设置的 属性 时抛出 KeyError
而不是返回 None
的基本原理和含义是什么?
我知道语言设计涉及权衡,其中一些基于实际问题,一些主要基于哲学。从计算机科学的角度来看,为什么 Python 是这样设计的?
提出问题的几个注释
- 我主要想知道这是否是一个实际的决定(即,它为开发人员提供了更多从错误中恢复的选择,而不仅仅是返回
None
),或者这个选择是否具有哲学意义(即, "accessing an unset property is, in Guido's opinion, an exceptional case"). - Python 的
dict.get
函数具有此功能,但文字符号似乎针对程序认为未设置值异常的情况进行了优化。 - 值得注意的是,异常处理被一些人认为是难以推理的,因为它涉及 a control flow that is distinct from the main program
- 如果 Python 返回
None
而不是引发异常,程序仍然可以在访问其值之前检查键是否存在,如果它确实想要处理未设置和None
价值观不同。但是由于我们无法访问异常编程流程,我很想知道我们可能会失去什么?
首先,让我们回顾一下我们的术语:
...when accessing an unset property on a Python dictionary
您没有访问 属性。您正在检索密钥。这就是为什么它被称为 KeyError
。属性是属性的特例,这就是为什么它们会引发 AttributeError
而不是
至于为什么,还要感谢类型论。我有一本字典 d
,其中(比如说)字符串作为键,整数作为值。对于 any k
,这条规则确实应该成立:
assert isinstance(d[k], int)
如果您 return None
,那么在这种情况下规则将失败。引发异常是表明您无法满足操作的后置条件的正常方式。在这种情况下,断言没有机会触发,因为在表达式仍在计算时引发了异常。
- Errors should not be fatal. That is, user code should be able to recover from error conditions as long as the virtual machine is still functional.
- At the same time, errors should not pass silently (These last two items naturally led to the decision to use exceptions throughout the implementation.)
还有一个经典案例,不喜欢大喊大叫的语言被破坏了:None
并不总是意味着缺少密钥(这意味着消息被破坏),它也可能意味着可选的,但未设置密钥(没关系)。以JSON:
>>> j = json.loads('{ "key": null }')
>>> j['key']
>>> j['nokey']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'nokey'
使用 None
-默认情况下,此示例变得更加笨拙。以 PHP 为例:
json_decode
returns null for invalid input, even though null is also a perfectly valid object for JSON to decode to—this function is completely unreliable unless you also calljson_last_error
every time you use it.
一个dict
可以存储任何值(包括None
)所以唯一的方法(当使用[]
语法时)来区分在丢失的密钥和映射到您认为的任何内容的密钥之间
将是一个很好的哨兵是提出一个例外。
>>> d = {'foo': None}
>>> print d['foo']
None
>>> print d['bar']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'bar'
对于您不关心区别的情况,您可以改用 get
方法,它故意 returns None
(或您选择的标记)用于不存在的键。
>>> print d.get('bar')
None
>>> print d.get('bar', 7)
7