如何解决用特定类型的值表示 Enum 的正确方法
我试图表示一个枚举,其中每个键的值必须具有特定类型。例如,我已经定义了这个:
from enum import Enum,auto
from typing import Any
class MatchType(Enum):
NATIVE = auto()
DICTIONARY = auto()
LIST = auto()
class MatchResult:
type: MatchType
value: Any
def __init__(self,type: MatchType,value: Any):
self.type = type
self.value = value
现在我如何将这些类型关联到相应的值类型?我的意思是,如果一个函数返回一个 MatchResult
with type = MatchType.NATIVE
我想 Mypy 检查我使用的是 float | int | string | bool
作为值:
def fn_returs_primitive() -> MathResult:
...
return MathResult(MatchType.NATIVE,[1,2,3]) # This CAN NOT happen as NATIVE should be int,float,string or boolean,NOT a list
我如何确保在 Python 中做到这一点?
例如,在 Rust 中,您可以定义一个 Enum,其中每个类型都有参数:
use std::collections::HashMap;
enum MatchType<T> {
NATIVE(u32),DICTIONATY(HashMap<String,T>),LIST(Vec<T>)
}
Python 中是否存在类似的东西?任何形式的帮助将不胜感激
解决方法
Python 有一个未标记的联合类型,称为 Union
。这种类型被认为是未标记的,因为没有信息存储选择了枚举的哪个变体。对于您的用例,这是一个未标记的实现:
from typing import TypeVar,Union
T = TypeVar("T")
MatchType = Union[int,dict[str,T],list[T]]
def get() -> MatchType:
return [1,2,3]
def match_on(match_type: MatchType):
if isinstance(match_type,int):
print("An int.")
elif isinstance(match_type,dict):
print("A dict.")
elif isinstance(match_type,list):
print("A list.")
但是请注意,我们必须在匹配过程中遍历所有可能的 MatchType
。这是因为没有与未标记联合的变体一起存储的标记,我们可以通过这些标记来索引地图/表。进行恒定时间匹配的幼稚尝试可能如下所示:
def match_on(match_type: MatchType):
{
int: lambda: print("An int."),dict: lambda: print("A dictionary."),list: lambda: print("A list.")
}[type(match_type)]()
但是给定一个 int
的子类,这会抛出一个 IndexError
,因为类型不是严格的 int
。
为了像 rust 编译器为了匹配标记联合而启用恒定时间匹配,您必须像这样模拟标记联合:
from dataclasses import dataclass
from typing import TypeVar,final,Generic,Union
T = TypeVar("T")
@final
@dataclass
class MatchNative:
value: int
@final
@dataclass
class MatchDictionary(Generic[T]):
value: dict[str,T]
# Avoid collision with built in type `List` by prepending `Match`.
@final
@dataclass
class MatchList(Generic[T]):
value: list[T]
MatchType = Union[MatchNative,MatchDictionary[T],MatchList[T]]
def get():
return MatchList([1,3])
def match_on(match_type: MatchType):
{
MatchNative: lambda: print("An int."),MatchDictionary: lambda: print("A dictionary."),MatchList: lambda: print("A list.")
}[type(match_type)]()
@dataclass
注释不是必需的,它只是为我随后在 __init__
函数中使用的标签创建一个 get
。
在这里,我们创建了三个类,其中包含每种类型的相关数据,同时由于引入了额外的间接层,它们本身也用作标记。这些类被创建为 @final
以排除作为 Union
实例给出的标签的子类。 @final
注释启用恒定时间匹配。
请注意,未标记和标记的实现仍然缺少详尽性检查,而 rust 的 match
语句具有。 Python 3.10 带有 match
语句,但我还没有研究 mypy 是否能够使用它执行详尽检查。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。