从常量变量创建文字类型

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

我想在我的类型定义中使用“常量”变量,如下所示:

FOO = "foo"
BAR = "bar"

@dataclass
class Event():
    name: Literal[FOO, BAR]

但是对于 mypy 来说这是非法代码。

这可行,但是我无法在代码中使用变量:

FOO = Literal["foo"]
BAR = Literal["bar"]

@dataclass
class Event():
    name: FOO | BAR

Event(FOO) # gives: Event(name=typing.Literal['foo'])

有没有办法让它工作而无需定义 FOO 和 BAR 两次?


Impelenting Marks 答案(https://stackoverflow.com/a/76382060/5386216)有效,但是 mypy 无法根据事件名称区分类型:

class FooBar(Enum):
    FOO = "foo"
    BAR = "bar"

@dataclass
class StringEvent:
    name: Literal[FooBar.FOO]
    value: str


@dataclass
class NumberEvent:
    name: Literal[FooBar.BAR]
    value: int


def handle_event(event: StringEvent | NumberEvent):
    if event.name == FooBar.FOO:
        event.value.upper()  # should not give a mypy error

    if event.name == FooBar.BAR:
        event.value.upper()  # Should give a mypy error

event.value.upper()
的两种情况都会给出以下 mypy 错误:
Item "int" of "Union[str, int]" has no attribute "upper"

python mypy python-typing
1个回答
0
投票

问题是类型和值不一样。一种是

Literal
类型,值为字符串。

就像@STerliakov在评论中提到的那样,最好的方法是颠倒过来,首先定义类型,然后根据类型定义常量字符串,如下所示:

from typing import Literal, get_args

FOO_TYPE = Literal["foo"]
FOO = get_args(FOO_TYPE)[0]
BAR_TYPE = Literal["bar"]
BAR = get_args(BAR_TYPE)[0]

这比我想要的要冗长一些,而且不太优雅,但至少你不会重复自己。人们也可以将其放入一个类中并编写一个装饰器来自动执行此操作:

from typing import Literal

def const_literals(cls):
    # make a copy of vars because we change the dictionary during iteration
    for const, lit in dict(**vars(c)).items():
        setattr(cls, f"{const}_TYPE", Literal[lit])
    return cls

@const_literals
class Constants:
    FOO = "foo"
    BAR = "bar"

print(Constants.FOO)  # prints "foo"
print(Constants.FOO_TYPE)  # prints "typing.Literal['foo']"
© www.soinside.com 2019 - 2024. All rights reserved.