如何注释可以转换为布尔值的参数?

How To Annotate an Argument That Can Be Cast As Boolean?

我开始接触 python 3.6 中的类型提示(又名注释),但我无法理解此功能的某些动态方面。

我写了以下代码,我想添加注释但不确定如何添加注释,即使在查看了有关类型提示的文档之后也是如此。

这是函数:

def validate_expression(expression: ?):
    try:
        assert expression
    except AssertionError as e:
        ...

expression 需要是断言起作用的任何东西(假设 bool(expression) 有效的任何表达式)。

我应该写什么来代替问号?

更新:

我知道大多数 python 表达式都可以转换为布尔值,但我编写此代码的上下文是合理期望表达式 not 可以断言。

我的相关例子是pandas.DataFrame。 运行 bool(pandas.DataFrame()) 引发错误,我有充分的理由期待有人可能会尝试将数据帧传递给验证函数。

更新 2: 根据 Chepner 的评论和回答,我现在明白了: 1. 在绝大多数情况下,任何 python 表达式都可以有效地转换为布尔值,这要么被 typing.Any 覆盖,要么根本不添加注释。 2. 在我感兴趣的极端情况下,即 bool(pandas.DataFrame()) # --> ValueError,注释无济于事,因为这是一个运行时错误。 3.如果还有其他与静态类型提示相关的边缘情况,我不知道。 4. 给定一个相关示例的 rarity/non-existence,没有开箱即用的类型一般只描述被转换为布尔值的能力的质量(类似于 typing.Iterable),据我所知我担心不值得向后弯腰来解决这种边缘情况(尽管听到相关示例和弯曲的解决方案会很有趣!)

任何值都可以在布尔上下文中使用。 object 的实例被认为是真值,除非后代 class 提供了替代定义;任何被认为是假的东西(比如一个空列表、一个空 str、一个空字典、False 本身等)之所以如此,是因为它被特别定义为如此。

因此,您唯一可以使用的类型提示是 typing.Any:

from typing import Any


def validate_expression(expression: Any):
    try:
        assert expression
    except AssertionError as e:
        ...

这实际上几乎不值得明确说明。

关于您的更新 2:这是完成您想做的事情的一种有点简陋的方法:

  1. 创建自定义 Protocol 匹配任何定义了 __bool__ 方法的类型。 (也许还有 __nonzero__ 方法,如果您还想支持 Python 2。)
  2. 为 pandas 库查找或创建存根。确保 DataFrame do not 的类型提示包含 __bool__ 方法。也就是说,不会匹配协议。
  3. 创建一个使用自定义协议作为类型提示的函数。

例如:

# If you're using Python 3.8+
from typing import Protocol

# If you're not, run 'pip install typing_extensions' and do the below instead
from typing_extensions import Protocol

class SupportsBool(Protocol):
    def __bool__(self) -> bool: ...

class MyFakeDataFrame:
    # ...snip...
    pass

class MyFakeBoolableThing:
    def __bool__(self) -> bool:
        return True

def validate_expression(x: SupportsBool) -> None:
    bool(x)

# These all type-check!

validate_expression(True)
validate_expression(0)
validate_expression(MyFakeBoolableThing())

# This will *not* typecheck

validate_expression(MyFakeDataFrame())

# Perhaps surprisingly, these will also not typecheck:

validate_expression("foobar")
validate_expression([1, 2, 3])

后两个表达式不会进行类型检查的原因是因为字符串和列表实际上都没有定义自定义 __bool__ 方法(或 Python 2 中的 __nonzero__ 方法):相反,他们定义了一个 __len__ 方法,如果 __bool__/__nonzero__ 不存在,bool(...) 函数将回退到检查 __len__

如果你确实希望你的验证函数接受这样的表达式,你需要使用像 Union[SupportsBool, SupportsInt] 这样的类型——但不幸的是,我相信 pandas DataFrame class 是否 实现了功能性 __len__ 方法,因此如果采用该方法,您将回到零。

所以基本上,在这种情况下,您要么 (1) 被迫拒绝某些类型,例如 str 或 list 在 bool 时做有意义的事情,要么 (2) 被迫接受 pandas.DataFrame 作为可接受的布尔值。