如何测试 __annotations__ 成员与泛型类参数具有相同的类型?

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

我希望能够仅选择与该基类中泛型参数具有相同类型的带注释成员:

from typing import Generic, TypeVar


T = TypeVar("T")

class MarkFinder(Generic[T]):
    def __init_subclass__(cls, **kwargs):
        cls.marked = tuple(
            name for name, annotated_type in cls.__annotations__.items() 
            if some_condition(annotated_type)
        )

所以如果我继承:

T2 = TypeVar("T2")

class Inheritor(MarkFinder[T2]):
    a: T2
    b: int
    c: T2

那么

Inheritor.marked
就应该是
('a', 'c')

我尝试将

some_condition(annotated_type)
替换为:

cls.__parameters__[0] is annotated_type
cls.__parameters__[0] == annotated_type

但即使它们具有相同的名称,类型也不相等。

什么是正确的条件?或者说这是不可能的?

python generics type-hinting python-typing
1个回答
0
投票

这是可能的,但有一些注意事项。下面的文章解释了该方法的关键以及一些需要记住的事情,所以我建议您先阅读它:

访问用户定义的 Generic[T] 类的任何特定子类中的类型参数

TL;DR 是从 __orig_bases__ 中获取original 基类(这将是

通用别名类型
)的类型参数,并将注释与其进行比较(用于标识)。

从您表达问题的方式来看,我假设您只希望这适用于类型变量,而不是特定类型参数。以下是您可以做到的方法:

from typing import Any, ClassVar, Generic, TypeVar, get_args, get_origin


T = TypeVar("T")


class MarkFinder(Generic[T]):
    marked: ClassVar[tuple[str, ...]] = ()

    @classmethod
    def __init_subclass__(cls, **kwargs: Any) -> None:
        super().__init_subclass__(**kwargs)
        for base in cls.__orig_bases__:  # type: ignore[attr-defined]
            origin = get_origin(base)
            if origin is None or not issubclass(origin, MarkFinder):
                continue
            type_arg = get_args(base)[0]
            if not isinstance(type_arg, TypeVar):
                return  # do not touch non-generic subclasses
            cls.marked += tuple(
                name for name, annotation in cls.__annotations__.items()
                if annotation is type_arg
            )

使用演示:

T2 = TypeVar("T2")


class Child(MarkFinder[T2]):
    a: T2
    b: int
    c: T2


T3 = TypeVar("T3")


class GrandChild(Child[T3]):
    d: T3
    e: str


class SpecificDescendant1(GrandChild[int]):
    f: int


class SpecificDescendant2(GrandChild[str]):
    f: float


print(Child.marked)                # ('a', 'c')
print(GrandChild.marked)           # ('a', 'c', 'd')
print(SpecificDescendant1.marked)  # ('a', 'c', 'd')
print(SpecificDescendant2.marked)  # ('a', 'c', 'd')

检查

type_arg
是否不是
TypeVar
实例很重要。如果没有它,该
SpecificDescendant1
子类的
'f'
元组中也会有
marked
。 (如果这是您想要的,只需从基类的
__init_subclass__
方法中删除该检查即可。)

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