为什么Python对KeyError和IndexError的处理不同?
我在尝试一些一行代码的解决方案,目的是定义一个变量,前提是这个变量还不存在。结果我发现,Python对字典和列表/元组的处理方式不一样。这些错误在我看来是相似的,所以我有点困惑,为什么会有这样的差异。
字典的KeyError处理
existing_dict = {"spam": 1, "eggs": 2}
existing_dict["foo"] = existing_dict["foo"] if not KeyError else 3
返回 {"spam": 1, "eggs": 2, "foo": 3}
注意,我在左右两边都引用了一个不存在的键;Python在这两种情况下处理KeyError都没有问题。
列表的IndexError处理(元组也是如此)
existing_list = ["spam","eggs"]
existing_list[2] = existing_list[2] if not IndexError else ["foo"]
返回 IndexError: list assignment index out of range
解决这个特定错误并不难(这里有答案),但我很好奇为什么这两种情况会有差别。在这两种情况下,似乎在赋值的两边都有错误,但只有一个“如果不”错误捕捉。
1 个回答
在这两种情况下,KeyError
和IndexError
都是类,并且它们都是“真”的:
>>> bool(KeyError)
True
>>> bool(IndexError)
True
在Python中,所有的类对象都被视为“真”,具体可以参考真值测试。
你不能用条件表达式来测试异常;对于你给出的两个例子,else
的值总是会被选中,然后被赋值;你的测试实际上等同于:
existing_dict["foo"] = 3
existing_list[2] = ["foo"]
你应该使用异常处理,或者使用长度测试。
异常的产生是因为对列表索引的赋值只有在索引已经存在时才有效:
>>> empty = []
>>> empty[0] = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
这只是字典和列表工作方式的不同;列表可以添加元素,这样索引的数量会增加。而字典则不行(没有顺序),所以要添加一个新的键值对,你需要直接给它赋值。另一方面,如果列表支持任意索引的赋值,那么中间的所有索引会发生什么呢?如果列表是空的,但你给索引42赋值,那索引0到41会怎样?
你可以用try
/except
来捕获异常:
try:
existing_list[2] = "foo"
except IndexError:
existing.append('foo')
这会替换索引2处的现有值,或者如果索引还不存在,则会添加新的值。
你也可以尝试测试长度:
if len(existing_list) <= 3:
existing_list.append('foo')
只有在元素数量少于3个时,才会添加新的元素。
对于字典,测试键是否存在:
if 'foo' not in existing_dict:
existing_dict['foo'] = 3