Python 3.13 具有类型参数和继承的泛型类

问题描述 投票:0回答:1

我正在探索 Python 3.13 中的类型,但无法获得我想要的严格的通用类型提示。

下面的代码定义了一个通用 Predicate 类、两个具体子类和一个通用否定谓词。

class Predicate[T](Callable[[T], bool]):
    """
    Base class for predicates: a function that takes a 'T' and evaluates to True or False.
    """
    def __init__(self, _eval: Callable[[T], bool]):
        self.eval = _eval

    def __call__(self, *args, **kwargs) -> bool:
        return self.eval(*args, **kwargs)

class StartsWith(Predicate[str]):
    def __init__(self, prefix: str):
        super().__init__(lambda s: s.startswith(prefix))

class GreaterThan(Predicate[float]):
    def __init__(self, y: float):
        super().__init__(lambda x: x > y)

class Not[T](Predicate[T]):
    def __init__(self, p: Predicate[T]):
        super().__init__(lambda x: not p(x))

if __name__ == '__main__':
    assert StartsWith("F")("Foo")
    assert GreaterThan(10)(42)
    assert Not(StartsWith("A"))("Foo")
    assert Not(GreaterThan(10))(3)

这会导致错误:

Traceback (most recent call last):
  File "[...]/generics_demo.py", line 36, in <module>
    class StartsWith(Predicate[str]):
                     ~~~~~~~~~^^^^^
  File "<frozen _collections_abc>", line 475, in __new__
TypeError: Callable must be used as Callable[[arg, ...], result].

当使用

class StartsWith(Predicate):
(即任何谓词)时,它可以工作,但对于我的口味来说,它的定义太宽松了。

关于如何进行此操作有任何提示吗?

python generics inheritance typing python-3.13
1个回答
0
投票

如果您将谓词定义更改为:

class Predicate[T]:

它有效。

我认为这是因为“新风格”泛型继承自旧的泛型类,为了向后兼容,这意味着您的类在内部看起来像这样:

class Predicate(Callable[[T], bool], Generic[T]):

并且由于多重继承逻辑,typing.Callable 泛型逻辑会覆盖您的自定义泛型逻辑。 这意味着如果您不更改 Predicate 类定义,则必须将继承从

Predicate[str]
更改为
Predicate[[str], bool]
等等。因为 Callable 估计的是一个类型列表,后跟一个类型。


顺便说一句:你不必从 callable 继承,它只是用于鸭子类型的类型提示,这意味着,你只需要 callable 来定义你期望的函数,而不是定义你是哪个函数。

这意味着

更通用的方法是这样做:

class Not[T](Predicate[T]):
    def __init__(self, p: Callable[[T], bool]):
        super().__init__(lambda x: not p(x))

if __name__ == '__main__':
    assert Not(StartsWith("A"))("Foo")
    assert Not(GreaterThan(10))(3)
    assert Not(lambda x: False)(4)

因为 Not 期望一个可调用的,其行为类似于谓词,并且没有谓词,所以 lambda 函数也被接受。

但这只是一个旁注。如果您有特殊用例,请忽略它。

© www.soinside.com 2019 - 2024. All rights reserved.