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

V.K*_*.K. 4 python exception-handling exception

我的问题是关于异常处理的最佳(最"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]
Run Code Online (Sandbox Code Playgroud)

我的问题是:根据我们是按索引还是按名称访问项目,__getitem__方法会引发IndexErrorKeyError.这是提高例外的好方法吗?此方法的调用者必须捕获这两种类型的异常.或者更好(更加pythonic所以说)捕获KeyErrorIndexError内部__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')
Run Code Online (Sandbox Code Playgroud)

另一方面,TypeError当我打电话collection[1.5]或打电话时似乎合乎逻辑collection[None].这是因为我觉得上述错误的解释是不同的.

我将不胜感激任何关于这个主题的评论或想法.

Fer*_*yer 6

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

根据我们是按索引还是按名称访问项目,__getitem__方法会引发IndexErrorKeyError.这是提高例外的好方法吗?

当然是.如果要同时支持索引和键访问,则应实现"序列"和"映射"接口,并根据方法的调用方式引发相应的异常.

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

例如,如果你把一个可以传递行为像您一样名单列表或其他对象的通用功能CollectionOfNamedItems,该功能将使用"序列接口",包括该合同__getitem__将引发一个IndexError无效的索引.如果你提出了另一种异常,你将违反合同,限制你的类的使用.

"映射界面"也是如此.

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

实际上,既然是子类KeyErrorIndexError子类LookupError,你的方法的调用者不区分这些情况可以而且应该简单地捕获LookupError:

try:
    item = collection[id]
except LookupError:
    # Could be KeyError or IndexError, we don't care.
Run Code Online (Sandbox Code Playgroud)