Python 类型提示与异常

发布于 2025-01-16 17:15:54 字数 420 浏览 0 评论 0原文

我有一个如下所示的函数:

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:

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

I have a function that looks like this:

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

    if 'error' in result:
        raise TypeError

    return False

In successful run of this function, I should get a bool, but if there is an error I should get a TypeError- which is OK because I deal with it in another function.

My function first line looks like this:

def check_for_errors(result: str) -> bool:

My question is: Should I mention the error in my type hinting?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

两仪 2025-01-23 17:15:54

类型提示不能说明任何有关异常的信息。它们完全超出了该功能的范围。但是,您仍然可以在文档字符串中记录异常。

来自 PEP 484 -- 类型提示 :

异常

没有建议列出显式引发的异常的语法。目前,此功能唯一已知的用例是文档,在这种情况下,建议将此信息放入文档字符串中。

Guido van Rossum 强烈反对在类型提示规范中添加例外,因为他不希望最终陷入需要检查异常(在调用代码中处理)或在每个级别显式声明的情况。

Type hinting can't say anything about exceptions. They are entirely out of scope for the feature. You can still document the exception in the docstring however.

From PEP 484 -- Type Hints:

Exceptions

No syntax for listing explicitly raised exceptions is proposed. Currently the only known use case for this feature is documentational, in which case the recommendation is to put this information in a docstring.

Guido van Rossum has strongly opposed adding exceptions to the type hinting spec, as he doesn't want to end up in a situation where exceptions need to be checked (handled in calling code) or declared explicitly at each level.

执着的年纪 2025-01-23 17:15:54

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

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

以下代码的灵感来自Rust 中完成异常处理的方式< /a>:它提供一个 Result 类型,可以是 OkErrOkErr 类都有一个 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,如果您考虑遵循这种方法,我建议您使用它。

There are good reasons for making the exception path part of the type annotations of your function, at least in certain scenarios. It just provides you more help from the type checker whenever you need to understand which exceptions the caller has to handle. (If you are interested in a more in-depth analysis, I wrote a blog post about this.)

As it is out of scope of the Python typing system to indicate which exceptions a function raises (like, for instance, in Java), we need a workaround to get this. Instead of raising, we can return the exception. That way, the exception becomes part of the function signature, and the caller has to handle it, leveraging the power of the type checker.

The following code is inspired by the way exception handling is done in Rust: It provides a Result type which can either be Ok or Err. Both Ok and Err classes have an unwrap() function, which either returns the wrapped value or raises the wrapped exception.

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 is a Generic, and it takes two types: the type of the Ok value, and the type of the Err exception. Here it is applied to your example:

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()

This is just a rough code sketch to demonstrate the principle. There is actually a more sophisticated library, poltergeist which I would recommend to use, if you consider following this approach.

我不咬妳我踢妳 2025-01-23 17:15:54

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

It is usually a good idea to document the error. This means that another developer using your function will be able to handle your errors without having to read through your code.

渡你暖光 2025-01-23 17:15:54

您可以使用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')

You can use NoReturn:
https://peps.python.org/pep-0484/#the-noreturn-type

from typing import NoReturn

def stop() -> NoReturn:
    raise RuntimeError('no way')

Or this:

from typing_extensions import NoReturn

def stop() -> NoReturn:
    raise RuntimeError('no way')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文