带有异常的 Python 类型提示
- 2025-03-13 08:48:00
- admin 原创
- 55
问题描述:
我有一个如下所示的函数:
def check_for_errors(result):
if 'success' in result:
return True
if 'error' in result:
raise TypeError
return False
如果该函数成功运行,我应该得到一个bool
,但如果出现错误,我应该得到一个TypeError
- 这是可以的,因为我在另一个函数中处理它。
我的函数第一行如下所示:
def check_for_errors(result: str) -> bool:
我的问题是:我应该在类型提示中提及错误吗?
解决方案 1:
类型提示无法说明任何有关异常的信息。它们完全超出了该功能的范围。不过,您仍然可以在文档字符串中记录异常。
来自PEP 484--类型提示:
例外
未提出用于列出显式引发的异常的语法。目前此功能的唯一已知用例是文档,在这种情况下建议将此信息放在文档字符串中。
Guido van Rossum强烈反对在类型提示规范中添加异常,因为他不希望最终出现需要在每个级别检查(在调用代码中处理)或明确声明异常的情况。
解决方案 2:
至少在某些情况下,将异常路径作为函数类型注释的一部分是有充分理由的。每当您需要了解调用者必须处理哪些异常时,它都会为您提供来自类型检查器的更多帮助。(如果您对更深入的分析感兴趣,我写了一篇关于此的博客文章。)
由于指示函数引发哪些异常超出了 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
是Generic
,它采用两种类型:值的类型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)
# Now, typechecker knows that r is `Result[bool, TypeError]`
if isinstance(r, Err):
# implement the error handling
else:
# implement the happy path
# If you do not want to handle the exception at this stage
def careless_method(result: list[str]):
check_for_errors(result).unwrap()
这只是粗略的代码草图,用于演示该原理。实际上,如果您考虑采用这种方法,我建议您使用更复杂的库poltergeist 。
解决方案 3:
记录错误通常是一个好主意。这意味着使用您的函数的其他开发人员将能够处理您的错误,而无需通读您的代码。
解决方案 4:
您可以使用 NoReturn:
https://peps.python.org/pep-0484/#the-noreturn-type
from typing import NoReturn
def stop() -> NoReturn:
raise RuntimeError('no way')
或者这样:
from typing_extensions import NoReturn
def stop() -> NoReturn:
raise RuntimeError('no way')
扫码咨询,免费领取项目管理大礼包!