Python类型提示有异常

Yuv*_*uss 32 python exception type-hinting python-3.x

我有一个看起来像这样的函数:

def check_for_errors(result):
    if 'success' in result:
        return True

    if 'error' in result:
        raise TypeError

    return False
Run Code Online (Sandbox Code Playgroud)

在成功运行此函数时,我应该得到一个bool,但是如果有错误我应该得到一个TypeError- 这是可以的,因为我在另一个函数中处理它.

我的函数第一行看起来像这样:

def check_for_errors(result: str) -> bool:
Run Code Online (Sandbox Code Playgroud)

我的问题是:我应该在提示中提及错误吗?

Mar*_*ers 36

类型提示不能说明异常.它们完全超出了该功能的范围.但是,您仍然可以在docstring中记录异常.

PEP 484 - 类型提示:

例外

没有提出列出明确引发的异常的语法.目前,此功能的唯一已知用例是documentational,在这种情况下,建议将此信息放在docstring中.

  • 或者检查您是否没有隐式引发人们意想不到的奇怪异常的用例 (4认同)
  • @ErikAronesty:定义*奇怪*。所有代码都会引发 [`MemoryError`](https://docs.python.org/3/library/exceptions.html#MemoryError),并且大多数代码都会引发 [`KeyboardInterrupt`](https://docs.python. org/3/library/exceptions.html#KeyboardInterrupt)。一个好的 linter 可以帮助您比类型提示更好地理解异常。 (3认同)
  • @shaunc:是的:[`typing.NoReturn`](https://docs.python.org/3/library/typing.html#typing.NoReturn)。 (3认同)
  • @EduardGrigoryev:“NotImplemented”*不是一个例外*,它是一个[单例对象](https://docs.python.org/3/library/constants.html#NotImplemented),一个特殊的标志值。您正在考虑 [`NotImplementedError`](https://docs.python.org/3/library/exceptions.html#NotImplementedError)。仍然可以返回异常,异常只是“BaseException”的子类,因此您可以使用“Type[BaseException]”来键入提示接受或返回异常类的函数。 (2认同)

jon*_*ach 21

将异常路径作为函数类型注释的一部分是有充分理由的,至少在某些情况下是这样。它只是在您需要了解调用者必须处理哪些异常时,从类型检查器中为您提供更多帮助。(如果您有兴趣进行更深入的分析,我写了一篇关于此的博客文章。)

由于指示函数引发哪些异常(例如在 Java 中)超出了 Python 类型系统的范围,因此我们需要一种解决方法来获取它。我们可以例外,而不是加注return。这样,异常就成为函数签名的一部分,调用者必须利用类型检查器的强大功能来处理它。

以下代码的灵感来自于Rust 中异常处理的方式:它提供了一个Result可以是Ok或 的类型Err。和OkErr都有一个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]
Run Code Online (Sandbox Code Playgroud)

Result是 a 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()
Run Code Online (Sandbox Code Playgroud)

这只是一个粗略的代码草图,用于演示原理。实际上有一个更复杂的库,poltergeist,如果您考虑遵循这种方法,我建议您使用它。

  • 我开发了一个像这样的轻量级库,非常重视类型安全:https://github.com/alexandermalyga/poltergeist (2认同)

Jim*_*ght 8

记录错误通常是个好主意。这意味着使用您的函数的其他开发人员将能够处理您的错误,而无需通读您的代码。