用特定类型的值表示 Enum 的正确方法
Correct way to represent an Enum with values of specific types
我正在尝试表示一个枚举,其中每个键的值都必须具有特定类型。例如,我定义了这个:
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
现在如何将这些类型关联到相应的值类型?我的意思是,如果函数 returns a 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 中,您可以定义一个枚举,其中每个类型都有参数:
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
。这是因为没有标签与我们可以索引 map / table 的未标记联合的变体一起存储。进行恒定时间匹配的幼稚尝试可能如下所示:
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, 2, 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
注释,它只是为我随后在 get
函数中使用的标签创建一个 __init__
。
在这里,我们创建了三个 类,其中包含每种类型的相关数据,同时由于引入了额外的间接层,它们本身也用作标签。这些 类 被制作成 @final
是为了排除作为 Union
实例给出的子 类 标签。 @final
注释启用恒定时间匹配。
请注意,未标记和标记的实现仍然缺少详尽检查,rust 的 match
语句具有。 Python 3.10 附带了一个 match
语句,但我还没有研究 mypy 是否能够用它执行详尽检查。
我正在尝试表示一个枚举,其中每个键的值都必须具有特定类型。例如,我定义了这个:
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
现在如何将这些类型关联到相应的值类型?我的意思是,如果函数 returns a 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 中,您可以定义一个枚举,其中每个类型都有参数:
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
。这是因为没有标签与我们可以索引 map / table 的未标记联合的变体一起存储。进行恒定时间匹配的幼稚尝试可能如下所示:
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, 2, 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
注释,它只是为我随后在 get
函数中使用的标签创建一个 __init__
。
在这里,我们创建了三个 类,其中包含每种类型的相关数据,同时由于引入了额外的间接层,它们本身也用作标签。这些 类 被制作成 @final
是为了排除作为 Union
实例给出的子 类 标签。 @final
注释启用恒定时间匹配。
请注意,未标记和标记的实现仍然缺少详尽检查,rust 的 match
语句具有。 Python 3.10 附带了一个 match
语句,但我还没有研究 mypy 是否能够用它执行详尽检查。