Dar*_*ath 3 python generics class-method python-3.x
我尝试在泛型类上调用类方法:
from typing import List, Union, TypeVar, Generic
from enum import IntEnum
class Gender(IntEnum):
MALE = 1
FEMALE = 2
DIVERS = 3
T = TypeVar('T')
class EnumAggregate(Generic[T]):
def __init__(self, value: Union[int, str, List[T]]) -> None:
if value == '':
raise ValueError(f'Parameter "value" cannot be empty!')
if isinstance(value, list):
self._value = ''.join([str(x.value) for x in value])
else:
self._value = str(value)
def __contains__(self, item: T) -> bool:
return item in self.to_list
@property
def to_list(self) -> List[T]:
return [T(int(character)) for character in self._value]
@property
def value(self) -> str:
return self._value
@classmethod
def all(cls) -> str:
return ''.join([str(x.value) for x in T])
Genders = EnumAggregate[Gender]
Run Code Online (Sandbox Code Playgroud)
但如果我打电话
Genders.all()
Run Code Online (Sandbox Code Playgroud)
我得到了错误TypeError: 'TypeVar' object is not iterable。因此 TypeVarT与 Enum 不正确匹配Gender。
我怎样才能解决这个问题?预期的行为是
>>> Genders.all()
'123'
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?或者这是不可能的?
Python 的类型提示系统用于静态类型检查器来验证代码,并且T只是类型系统的占位符,就像模板语言中的槽一样。它不能用作对特定类型的间接引用。
如果您想生成具体的实现,则需要对泛型类型进行子类化。因为Gender是一个类而不是实例,所以您还需要告诉类型系统您计划如何Type[T]在某个地方使用 a 。
因为您还希望能够用作(T用Enum()调用它EnumSubclass(int(character))),所以我还绑定了 typevar;这样,类型检查器将理解 的所有具体形式Type[T]都是可调用的,并将生成单独的T实例,而且这些T实例将始终具有一个.value属性:
from typing import ClassVar, List, Union, Type, TypeVar, Generic
from enum import IntEnum
T = TypeVar('T', bound=IntEnum) # only IntEnum subclasses
class EnumAggregate(Generic[T]):
# Concrete implementations can reference `enum` *on the class itself*,
# which will be an IntEnum subclass.
enum: ClassVar[Type[T]]
def __init__(self, value: Union[int, str, List[T]]) -> None:
if not value:
raise ValueError('Parameter "value" cannot be empty!')
if isinstance(value, list):
self._value = ''.join([str(x.value) for x in value])
else:
self._value = str(value)
def __contains__(self, item: T) -> bool:
return item in self.to_list
@property
def to_list(self) -> List[T]:
# the concrete implementation needs to use self.enum here
return [self.enum(int(character)) for character in self._value]
@property
def value(self) -> str:
return self._value
@classmethod
def all(cls) -> str:
# the concrete implementation needs to reference cls.enum here
return ''.join([str(x.value) for x in cls.enum])
Run Code Online (Sandbox Code Playgroud)
通过上面的通用类,您现在可以创建一个具体的实现,使用您Gender IntEnum安装到T插槽中并作为类属性:
class Gender(IntEnum):
MALE = 1
FEMALE = 2
DIVERS = 3
class Genders(EnumAggregate[Gender]):
enum = Gender
Run Code Online (Sandbox Code Playgroud)
为了能够将IntEnum子类作为类属性访问,我们需要使用typing.ClassVar[]; 否则类型检查器必须假设该属性仅在实例上可用。
因为Gender IntEnum子类本身就是一个类,所以我们也需要告诉类型检查器这一点,因此使用typing.Type[].
现在Gender具体子类可以工作了;EnumAggregate[Gender]作为基类的使用告诉类型检查器在任何地方T都替换Gender,并且由于实现使用enum = Gender,类型检查器发现这确实正确满足并且代码通过了所有检查:
class Gender(IntEnum):
MALE = 1
FEMALE = 2
DIVERS = 3
class Genders(EnumAggregate[Gender]):
enum = Gender
Run Code Online (Sandbox Code Playgroud)
你可以调用Genders.all()来生成一个字符串:
$ bin/mypy so65064844.py
Success: no issues found in 1 source file
Run Code Online (Sandbox Code Playgroud)
请注意,我不会将枚举值存储为字符串,而是存储为整数。在这里来回转换它没有什么价值,并且您将自己限制为值在 0 到 9(个位数)之间的枚举。