如何将 "value" 属性强类型化为 str 或自定义类型?

How to strongly type the "value" attribute to be str or a custom type?

在严格类型检查模式下使用 Pylance (ms-python.vscode-pylance) VS Code 扩展时,我在以下代码的自定义枚举值上遇到类型错误:

def println_ctrl_sequence(message: str, ctrlSequence: Union[ANSICtrlSequence, str]):
    """
    This function is use with  terminals to print the message
    with colors specified by a, ANSI control sequence that 
    can be either a str or a console.ANSICtrlSequence object.
    """
    if type(ctrlSequence) == ANSICtrlSequence:
        ctrlSequenceStr: str = ctrlSequence.value
    else:
        ctrlSequenceStr = ctrlSequence
    
    print("%s%s%s" % (
        ctrlSequenceStr,
        message,
        ANSICtrlSequence.RESET.value
    ))

ctrlSequenceStr: str = ctrlSequence.value 行检测到类型错误,因为 ctrlSequence.value 被检测为 Any | Unknown 类型。所以我的objective是强类型我的扩展Enumvalue属性:

# python enum : https://docs.python.org/3/library/enum.html 
from enum import Enum

class ANSICtrlSequence(Enum):

    # basic control sequences
    RESET = "3[m" 

    # full control sequences
    PASSED = "3[1;4;38;5;76m" 
    FAILED = "3[1;5;38;5;197m" 

我已经尝试过一些事情,例如按照指定 ANSICtrlSequence(str, Enum) 但没有成功。

我已经阅读了 class enum.pyi 我可以理解为什么值的类型是这样的:

class Enum(metaclass=EnumMeta):
    name: str
    value: Any
    ...

我找不到在 documentation or on 中的任何地方将我的值属性键入为 str 的方法。那有可能吗?有没有办法覆盖继承属性的类型?或者我是否需要扩展 Enum class 与例如可能是 StrEnum 的 IntEnum 的等价物?也许我需要编写自己的强类型枚举 class?有什么我遗漏的吗?

问题似乎并非完全 来自 Enum.value,而是来自 str 作为 ctrlSequence 的可能类型。 Pylance 似乎检查 Union 中包含的所有类型是否具有 .value 属性,而对于 str,当然,它没有 .value 所以 Pylance 没有不知道期望的类型(那是“未知”)。

我们可以在不使用 Enum:

的情况下重现 similar“未知”错误
x = 5
print(x.value)

在你的情况下,在定义你的 Enum 时遵循 解决方案并从 str 继承仍然是必要的,因为这表明类型检查器(这里是 Pylance)你的 Enum .valuestr 类型的。

所以,你肯定需要这个来强类型你的枚举:

class ANSICtrlSequence(str, Enum):
    RESET = "3[m" 
    PASSED = "3[1;4;38;5;76m" 
    FAILED = "3[1;5;38;5;197m" 

但是,它仍然显示为

Type of "value" is partially unknown
^ Type of "value" is "Any | Unknown*"

因为在Union[ANSICtrlSequence, str]中,ANSICtrlSequence.value的类型是Anystr.value的类型是Unknown .当您将 Union 的顺序反转为 Union[str, ANSICtrlSequence],然后变为

时,str 的这个问题就很明显了

 Type of "value" is "Unknown | Any"

...表示“未知”与 str 相关联。基本上,我的观点是 你不应该专注于键入枚举的 .value 属性,因为问题在于包含 str。如果您删除 Union 并仅使用 ANSICtrlSequence:

,错误实际上会消失
class ANSICtrlSequence(str, Enum):
    RESET = "3[m" 
    PASSED = "3[1;4;38;5;76m" 
    FAILED = "3[1;5;38;5;197m" 

def println_ctrl_sequence(message: str, ctrlSequence: ANSICtrlSequence):
    # Pylance does not complain here
    ctrlSequenceStr: str = ctrlSequence.value

...首先表明您的 Enum 没有任何问题。

但我理解为什么代码中有一个 Union。不幸的是,Pylance 没有。它不明白,当代码到达 ctrlSequence.value 时,代码已经检查过 ctrlSequence 是一个 Enum.

有趣的是,起作用的是更改检查类型的方式。而不是 type(obj), use isinstance(obj, classinfo):

class ANSICtrlSequence(str, Enum):
    RESET = "3[m" 
    PASSED = "3[1;4;38;5;76m" 
    FAILED = "3[1;5;38;5;197m" 

def println_ctrl_sequence(message: str, ctrlSequence: Union[ANSICtrlSequence, str]):
    if isinstance(ctrlSequence, ANSICtrlSequence):
        ctrlSequenceStr: str = ctrlSequence.value
    else:
        ctrlSequenceStr = ctrlSequence

...满足 Pylance 并修复错误:)

我希望这不是“它适用于我的环境”的情况,但我几乎总是使用 isinstance 而不是 type检查对象的类型,并且我的代码中没有出现枚举或联合与枚举的任何 Pylance 错误。我不知道 Pylance 是如何工作的,但这里有一个关于该主题的相关问答:What are the differences between type() and isinstance()?

如果你确实需要使用type(ctrlSequence),那么你可以使用typing.cast,即:

Cast a value to a type.

This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything (we want this to be as fast as possible).

from typing import Union, cast

class ANSICtrlSequence(str, Enum):
    RESET = "3[m" 
    PASSED = "3[1;4;38;5;76m" 
    FAILED = "3[1;5;38;5;197m" 

def println_ctrl_sequence(message: str, ctrlSequence: Union[ANSICtrlSequence, str]):
    if type(ctrlSequence) == ANSICtrlSequence:
        ctrlSequence = cast(ANSICtrlSequence, ctrlSequence)
        ctrlSequenceStr: str = ctrlSequence.value
    else:
        ctrlSequenceStr = ctrlSequence

...再次满足 Pylance 并修复错误 :) cast 强制执行类型检查器(这里是 Pylance),ctrlSequence 是您的枚举类型,.value确实是一个字符串。