如何避免类型注释中重复文字

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

挑战:定义文字(及其函数注释的类型),但只写入一次文字值。

现实生活中的示例: 端口可以分配一个 VLAN 或 VLAN 列表,或者可以定义为中继,也可以不分配。 我们需要“主干”、“未分配”常量。 VLAN 定义为

class VlanId(int): ...
我们还需要类型注释函数的类型定义,例如:

  • get_init_port_value()
    返回单个 VLAN 或“中继”(不允许 VLAN 列表或“未分配”)
  • get_port_status(port)
    返回给定端口上的当前分配

尝试1:分别定义常量值和类型。可以用,但是很难看。

Trunk = Literal["trunk"]
TRUNK = "trunk"  # TRUNK type becomes Literal[“trunk”]
# NB, TRUNK: Trunk = “trunk” provides the exact same thing.
Unallocated = Literal["unallocated"]
UNALLOCATED = "unallocated"  # UNALLOCATED type becomes Literal[“unallocated”]
PortVlanAllocation = VlanId | list[VlanId] | Trunk | Unallocated
def get_init_port_value() -> VlanId | Trunk: ...
def get_port_status(port: Port) -> PortVlanAllocation: ...

尝试2:使用Final类型。就像Try1一样,它可以工作,但很难看。并且Type仍然不起作用,就像Try3一样。

..
TRUNK: Final = "trunk"  # TRUNK type becomes Literal[“trunk”]
..

尝试3:从定义的常量中获取类型。不起作用,因为 Type 只能告诉类的类型。

..
PortVlanAllocation = VlanId | list[VlanId] | Type[TRUNK] | Type[UNALLOCATED]
..

尝试4:再次从定义的常量中获取类型。不起作用,因为 Literal 不接受 TRUNK 和 UNALLOCATED,即使它们是文字。

..
PortVlanAllocation = VlanId | list[VlanId] | Literal[TRUNK] | Literal[UNALLOCATED]
..

尝试 5: 使用 enum 进行非 VLAN 分配。可行,实现了文字字符串仅写入一次,并且紧凑,但可能不是最好的。

class NonVlanAlloc(enum.Enum):
    TRUNK = "trunk"
    UNALLOCATED = "unallocated"
PortVlanAllocation = NonVlanAlloc | VlanId | list[VlanId]
def get_init_port_value() -> Literal[NonVlanAlloc.TRUNK] | VlanId: ...

我说它有效,因为 mypy 提供了以下有效和无效

get_init_port_value
返回语句的预期结果:

return NonVlanAlloc.UNALLOCATED # Incompatible return value type (got "Literal[NonVlanAlloc.UNALLOCATED]", expected "Union[Literal[NonVlanAlloc.TRUNK], VlanId]")
return "trunk"  # Incompatible return value type (got "Literal['trunk']", expected "Union[Literal[NonVlanAlloc.TRUNK], VlanId]")
return NonVlanAlloc.TRUNK  # OK
return 42  # Incompatible return value type (got "Literal[42]", expected "Union[Literal[NonVlanAlloc.TRUNK], VlanId]")
return VlanId(2345789789)  # OK

这个枚举解决方案看起来不错,但是如果我们没有 UNALLOCATED 怎么办?为单个 TRUNK 定义一个枚举有点大材小用,不是吗?

我想知道是否有一种方法可以获取 TRUNK 的类型(无需单独的显式定义),即我想在 Try3Try4 中实现的目标(以及在 Try5 中实现的某种实现)。这与在运行时从 python 文字类型中获取文字?相反,因为

  • 他们声明类型并计算出值,
  • 当我声明值(常量)并弄清楚它们的类型(例如函数注释)之后。
python enums python-typing literals typing
1个回答
0
投票

当我声明值(常量)并弄清楚它们的类型(例如函数注释)之后。

静态类型注释无法在事后创建。它们是静态的。 这就是为什么人们会首先实现

Literal
,然后动态创建变量。有一个相当不错的解释here

这似乎是采用 WET 方法的好地方。请参阅干与湿

© www.soinside.com 2019 - 2024. All rights reserved.