一种方法允许引发多种类型的异常或只是一种异常是否更好?

时间:2014-11-09 10:43:51

标签: python exception exception-handling

我的问题是关于最好的(大多数' 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__方法会引发IndexErrorKeyError。这是提高例外的好方法吗?此方法的调用者必须捕获这两种类型的异常。或者更好(更加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')

另一方面,当我呼叫TypeErrorcollection[1.5]时,似乎合理地抛出collection[None]。这是因为我觉得上述错误的解释是不同的。

我很感激有关此主题的任何评论或想法。

2 个答案:

答案 0 :(得分:6)

通常,您应该始终抛出最具体的异常。更重要的是,不要抛出旨在表示其他内容的异常类型。在您的情况下,ValueError显然是错误的异常类型。

  

取决于我们是按索引还是按名称访问项目,__getitem__方法会引发IndexErrorKeyError。这是提高例外情况的好方法吗?

是的,当然。如果你想同时支持索引和密钥访问,你应该实现" sequence"和"映射"接口,并根据您的方法调用方式引发相应的异常。

原因是抽象:您将对象设计为两者作为可以按索引访问其内容的序列/列表类型,并作为映射/可以按键访问其内容的字典类型。您班级的用户将选择一个这些接口,而不关心另一个。因此,他们期望不同类型的异常,并且不需要知道您的类的实现细节。

例如,如果您采用可传递列表的通用函数或其行为类似于CollectionOfNamedItems的列表的其他对象,则该函数将使用"序列接口"其中包括{{1>}将为无效索引引发__getitem__合同。如果你提出了另一种例外情况,你将违反合同,限制使用你的类

"映射界面"。

也是如此
  

此方法的调用者必须捕获这两种类型的异常。

实际上,由于IndexErrorKeyError子类IndexError,您的方法的调用者不区分这些情况可以而且应该只是捕获{{ 1}}:

LookupError

答案 1 :(得分:0)

没有大多数Pythonic捕捉异常的方法。建议捕获最具体的异常。但是,当您捕获多个异常时,请注意异常层次结构。所以你有最容易找到错误的方法。有时你不想捕获大多数特定的异常,但是你决定对特定的代码片段需要什么。