3

在严格类型检查模式下使用 Pylance (ms-python.vscode-pylance) VS Code 扩展时,我的自定义 Enum 值出现类型错误,如下代码:

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被检测为 type Any | Unknown。所以我的目标是强烈键入value我的扩展属性Enum

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

class ANSICtrlSequence(Enum):

    # basic control sequences
    RESET = "\033[m" 

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

我已经尝试过例如在“Python 中基于字符串的枚举”问答中ANSICtrlSequence(str, Enum)指定的操作,但没有成功。

我已经阅读了该课程enum.pyi,并且可以理解为什么 value 的类型是这样的:

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

我找不到在文档StackOverflow上的任何地方将我的 value 属性键入为 str 的方法。那么有可能吗?有没有办法覆盖继承属性的类型?或者我是否需要使用例如可能是 StrEnum 的 IntEnum 的等价物来扩展 Enum 类?也许我需要编写自己的强类型 Enum 类?有什么我错过的吗?

4

1 回答 1

1

似乎问题不完全来自于Enum.value包含. Pylance 似乎会检查包含在 中的所有类型是否具有属性,而对于,它当然没有,因此 Pylance 不知道期望什么类型(“未知”)。strctrlSequenceUnion.valuestr.value

我们可以在不使用 的情况下重现类似的“未知”错误Enum

x = 5
print(x.value)

没有枚举的类似错误

在您的情况下,遵循基于字符串的枚举str解决方案并在定义时继承自Enum仍然是必要的,因为这向类型检查器(此处为 Pylance)表明您的 Enum.value属于str-type。

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

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

但是,它仍然显示为

“值”的类型部分未知
^ “值”的类型是“任何|未知*”

因为在 中Union[ANSICtrlSequence, str].valuefor 的类型是,forANSICtrlSequence的类型Any是Unknown。当您将 Union 的顺序反转为 时,此问题很明显,然后变为.valuestrstrUnion[str, ANSICtrlSequence]

 “值”的类型是“未知|任何”

...表示“未知”与str. 基本上,我的观点是你不应该专注于输入.valueEnum 的属性,因为问题在于 includestr。如果您删除Union并仅使用,该错误实际上会消失ANSICtrlSequence

class ANSICtrlSequence(str, Enum):
    RESET = "\033[m" 
    PASSED = "\033[1;4;38;5;76m" 
    FAILED = "\033[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),使用isinstance(obj, classinfo)

class ANSICtrlSequence(str, Enum):
    RESET = "\033[m" 
    PASSED = "\033[1;4;38;5;76m" 
    FAILED = "\033[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

isinstance 没有错误

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

我希望这不是“它适用于我的环境”的情况,但我几乎总是在检查对象的类型时使用,isinstance而不是type在我的代码中使用 Enums 或带有 Enums 的 Unions 时没有任何 Pylance 错误。我不知道 Pylance 是如何工作的,但这里有一个关于该主题的相关问答:type() 和 isinstance() 之间有什么区别?

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

将值转换为类型。

这将返回不变的值。对于类型检查器,这表明返回值具有指定的类型,但在运行时我们故意不检查任何内容(我们希望它尽可能快)。

from typing import Union, cast

class ANSICtrlSequence(str, Enum):
    RESET = "\033[m" 
    PASSED = "\033[1;4;38;5;76m" 
    FAILED = "\033[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

使用 cast 没有错误

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

于 2021-07-27T12:11:52.147 回答