在某些情况下,将异常路径作为函数类型注释的一部分是有好处的。这样可以在需要了解调用者必须处理哪些异常时,从类型检查器中获得更多帮助(如果您对更深入的分析感兴趣,我写了一篇博客文章)。
由于Python类型系统无法指示函数引发的异常(例如Java),我们需要一个解决方法来解决这个问题。我们可以使用return
而不是引发异常。这样,异常就成为函数签名的一部分,调用者必须处理它,利用类型检查器的强大功能。
以下代码受到
Rust 中异常处理的启发:它提供了一个
Result
类型,可以是
Ok
或
Err
。
Ok
和
Err
都有一个
unwrap()
函数,它可以返回包装的值或引发包装的异常。
from typing import Generic, TypeVar, NoReturn
OkType = TypeVar("OkType")
ErrType = TypeVar("ErrType", bound=Exception)
class Ok(Generic[OkType]):
def __init__(self, value: OkType) -> None:
self._value = value
def unwrap(self) -> OkType:
return self._value
class Err(Generic[ErrType]):
def __init__(self, exception: ErrType) -> None:
self._exception = exception
def unwrap(self) -> NoReturn:
raise self._exception
Result = Ok[OkType] | Err[ErrType]
Result
是一个泛型,它有两个类型参数:表示 Ok
值的类型和表示 Err
异常的类型。这里将其应用于你的示例:
def check_for_errors(result: list[str]) -> Result[bool, TypeError]:
if 'success' in result:
return Ok(True)
if 'error' in result:
return Err(TypeError())
return Ok(False)
def careful_method(result: list[str]):
r = check_for_errors(result)
if isinstance(r, Err):
else:
def careless_method(result: list[str]):
check_for_errors(result).unwrap()
这只是一个简单的代码草图,用于演示原理。如果您考虑采用这种方法,实际上有一个更复杂的库poltergeist,我建议使用它。
MemoryError
,大多数代码可以引发KeyboardInterrupt
。一个好的语法检查器可以帮助你更好地理解异常,比类型提示更有用。 - Martijn PietersNotImplemented
不是一个异常,它是一个单例对象,是一个特殊的标志值。你可能在想NotImplementedError
。异常仍然可以被返回,异常只是继承了BaseException
的类,因此可以使用Type[BaseException]
来类型提示接受或返回异常类的函数。 - Martijn Pieterstyping.NoReturn
。 - Martijn Pieters