在 Python 中扩展 Generic.__class_getitem__ 以接受更多参数

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

如何为 Python

__class_getitem__
类扩展
Generic
?我想向
__class_getitem__
添加参数,同时将一些参数向上传播到
Generic.__class_getitem__

请参阅下面的代码片段以获取示例用例(未运行):

from typing import ClassVar, Generic, TypeVar

T = TypeVar("T")

class Foo(Generic[T]):
    cls_attr: ClassVar[int]

    def __class_getitem__(cls, cls_attr: int, item):
        cls.cls_attr = cls_attr
        return super().__class_getitem__(item)

    def __init__(self, arg: T):
        pass

foo = Foo[1, bool](arg=True)

给我这个

TypeError

Traceback (most recent call last):
  File "/path/to/file.py", line 17, in <module>
    foo = Foo[1, bool](arg=True)
TypeError: Foo.__class_getitem__() missing 1 required positional argument: 'item'
python generics python-typing metaclass
3个回答
3
投票

正如 @juanpa.arrivilillaga 所建议的,这就是要走的路:

from typing import ClassVar, Generic, TypeVar

T = TypeVar("T")

class Foo(Generic[T]):
    cls_attr: ClassVar[int]

    def __class_getitem__(cls, item: tuple[int, T]):
        cls.cls_attr = item[0]
        return super().__class_getitem__(item[1])

    def __init__(self, arg: T):
        self.arg = arg


foo = Foo[1, bool](arg=True)
assert foo.cls_attr == 1
assert foo.arg

不幸的是,Python 类型检查工具似乎还不够先进,无法理解这种模式。例如,

mypy==0.971
(2022 年 9 月)尚不支持
__class_getitem__
,根据 https://github.com/python/mypy/issues/11501


0
投票

如果您希望有两个通用变量,那么您可以这样做:

from typing import Generic, TypeVar

T = TypeVar("T")
V = TypeVar("V")

class Foo(Generic[T, V]):
    
    def __init__(self, arg: T):
        pass

foo: Foo[int, bool] = Foo(arg=True)

0
投票

我认为您可能正在寻找元类:

class CustomMeta(type):

    def __getitem__(my_type_that_has_this_meta, my_values_passed_as_tuple_if_needed):
        # do whatever
        return WhateverTypeThatMatchesAnnotation()

class MyClass(metaclass=CustomMeta):
     ...
        
if __name__ == "__main__":
    whatever = MyClass['hi', 'hello', 'hallo', 'bonjour']
    assert isinstance(whatever, WhateverTypeThatMatchesAnnotation)

这使用了这样一个事实:就 Python 所知,元类是类的类型。

__class_getitem__
更漂亮,但在某些情况下,没有匹配的 typevar
T
,我们只想传递非类型。在这些情况下,破解语言!

编辑:

我一直在研究它,并且由于它在 mypy 中也不起作用,因此您还可以使用

typing.TYPE_CHECKING
来绕过检查。例如,您可以创建以下代码

from typing import TYPE_CHECKING

class WhateverTypeThatMatchesAnnotation: ...


if not TYPE_CHECKING:
    # python runs this
    class CustomMeta(type):

        def __getitem__(my_type_that_has_this_meta, my_values_passed_as_tuple_if_needed):
            # do whatever
            return WhateverTypeThatMatchesAnnotation()

    class MyClass(metaclass=CustomMeta):
        ...
else:
    # mypy runs this
    class CustomMeta:
        def __getitem__(self, item):
            return WhateverTypeThatMatchesAnnotation()
    MyClass = CustomMeta()


if __name__ == "__main__":
    whatever = MyClass['hi', 'hello', 'hallo', 'bonjour']
    assert isinstance(whatever, WhateverTypeThatMatchesAnnotation)


感谢gsakkis引导我找到解决方案

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