我正在探索 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):
(即任何谓词)时,它可以工作,但对于我的口味来说,它的定义太宽松了。
关于如何进行此操作有任何提示吗?
如果您将谓词定义更改为:
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 函数也被接受。
但这只是一个旁注。如果您有特殊用例,请忽略它。