我的问题是关于最好的(大多数' pythonic'方式)异常处理,以防方法可以引发两种(或更多)类型的异常但是它们的解释是相同的。来电者。
假设我有一个named(name is string)对象的集合。我希望这个集合能够通过索引或名称返回项目。
class CollectionOfNamedItems:
def __init__(self, items):
self._dict = {item.name: item for item in items}
self._items = tuple(items)
def __getitem__(self, item):
if isinstance(item, str):
return = self._dict[item] # may raise KeyError
return self._items[item] # may raise IndexError
# usage: collection['X'] or collection[1]
我的问题是:根据我们是按索引还是按名称访问项目,__getitem__
方法会引发IndexError
或KeyError
。这是提高例外的好方法吗?此方法的调用者必须捕获这两种类型的异常。或者更好(更加pythonic所以说)在KeyError
内抓住IndexError
和__getitem__
并提升ValueError
(或其他一些?)以便调用者可以捕获无论传递的参数类型如何,都只是一种类型的异常。
def __getitem__(self, item):
try:
if isinstance(item, str):
return = self._dict[item] # may raise KeyError
return self._items[item] # may raise IndexError
except (KeyError, IndexError):
raise ValueError('invalid item')
另一方面,当我呼叫TypeError
或collection[1.5]
时,似乎合理地抛出collection[None]
。这是因为我觉得上述错误的解释是不同的。
我很感激有关此主题的任何评论或想法。
答案 0 :(得分:6)
通常,您应该始终抛出最具体的异常。更重要的是,不要抛出旨在表示其他内容的异常类型。在您的情况下,ValueError
显然是错误的异常类型。
取决于我们是按索引还是按名称访问项目,
__getitem__
方法会引发IndexError
或KeyError
。这是提高例外情况的好方法吗?
是的,当然。如果你想同时支持索引和密钥访问,你应该实现" sequence"和"映射"接口,并根据您的方法调用方式引发相应的异常。
原因是抽象:您将对象设计为两者作为可以按索引访问其内容的序列/列表类型,并作为映射/可以按键访问其内容的字典类型。您班级的用户将选择一个这些接口,而不关心另一个。因此,他们期望不同类型的异常,并且不需要知道您的类的实现细节。
例如,如果您采用可传递列表的通用函数或其行为类似于CollectionOfNamedItems
的列表的其他对象,则该函数将使用"序列接口"其中包括{{1>}将为无效索引引发__getitem__
的合同。如果你提出了另一种例外情况,你将违反合同,限制使用你的类。
"映射界面"。
也是如此此方法的调用者必须捕获这两种类型的异常。
实际上,由于IndexError
和KeyError
子类IndexError
,您的方法的调用者不区分这些情况可以而且应该只是捕获{{ 1}}:
LookupError
答案 1 :(得分:0)
没有大多数Pythonic捕捉异常的方法。建议捕获最具体的异常。但是,当您捕获多个异常时,请注意异常层次结构。所以你有最容易找到错误的方法。有时你不想捕获大多数特定的异常,但是你决定对特定的代码片段需要什么。