Python 例外准则解释

Python Exception guidelines explanation

在 Google 的 python 样式指南文档中,他们提到了以下内容:

Note that this raising of ValueError is not mentioned in the doc string's "Raises:" section because it is not appropriate to guarantee this specific behavioral reaction to API misuse.

为什么不合适?这是否意味着首先不应像这样使用 ValueError?

def connect_to_next_port(self, minimum: int) -> int:
"""Connects to the next available port.

Args:
  minimum: A port value greater or equal to 1024.

Returns:
  The new minimum port.

Raises:
  ConnectionError: If no available port is found.
"""
if minimum < 1024:
  # Note that this raising of ValueError is not mentioned in the doc
  # string's "Raises:" section because it is not appropriate to
  # guarantee this specific behavioral reaction to API misuse.
  raise ValueError(f'Min. port must be at least 1024, not {minimum}.')
port = self._find_next_open_port(minimum)
if not port:
  raise ConnectionError(
      f'Could not connect to service on port {minimum} or higher.')
assert port >= minimum, (
    f'Unexpected port {port} when minimum was {minimum}.')
return port

ConnectionError 可能是根据记录的用法使用该函数的结果。

然而,ValueError 只是公然违反函数前提条件的结果。您已被警告 minimum >= 1024 必须为真。您不需要记录违反该警告的后果。

例如,您不需要 try 语句来处理 ValueError;您可以在调用函数之前检查参数的值以避免它。 (在这种情况下,请求宽恕比获得许可容易。)

您确实需要 try 语句来处理 ConnectionError,因为无法预测它可能会发生。为了知道可能会引发 ConnectionError,需要对其进行记录。


从具有静态类型检查的整体语言的角度来看,区别在于您可以通过使用适当的 argument 类型来避免的错误,以及您可以通过使用来避免的错误适当的 return 类型。考虑 Haskell 类伪代码中的部分函数。

type ValidPort = Int
connect_to_next_port :: ValidPort -> ValidPort
connect_to_next_port = ...

但并非所有 Int 都是有效端口;只有 1024 和 65535 之间的整数(TCP/IP 中的端口是 16 位值)。所以想象我们有一种方法来定义受限类型,我们可以用

消除 ValueError
type ValidPort = { x :: Int | 1024 <= x <= 65535 }
connect_to_next_port :: ValidPort -> ValidPort
connect_to_next_port = ...

但我们可能找不到 return 的端口。我们没有引发异常,而是 return 类型 Maybe ValidPort 的东西,您可以将其视为包含类型 ValidPort 和值 None 的包装器(大致对应于Haskell中的Nothing).

type ValidPort = { x :: Int | 1024 <= x <= 65535 }
connect_to_next_port :: ValidPort -> Maybe ValidPort
connect_to_next_port = ...

总而言之,我们记录了可以编码为 return 类型的异常,而不是可以用适当的参数类型消除的异常。