我希望能够仅选择与该基类中泛型参数具有相同类型的带注释成员:
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
但即使它们具有相同的名称,类型也不相等。
什么是正确的条件?或者说这是不可能的?
这是可能的,但有一些注意事项。下面的文章解释了该方法的关键以及一些需要记住的事情,所以我建议您先阅读它:
访问用户定义的 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__
方法中删除该检查即可。)