指定文本输出的Mypy

2024-09-29 06:32:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我的函数返回一个由两个值组成的元组,其中一个值是包含两个可能值之一的字符串,例如“a”或“b”。这个字符串是从URL中检索到的,实际上理论上可以是其他的东西——在这种情况下,我会提出一个Valuerror。然而,mypy似乎没有看到我的错误,仍然对字符串到文字的转换感到不安。解决这个问题的正确方法是什么

def _get_listing_info(txt: str) -> Tuple[int, Literal["a", "b"]]:
    
    findInfo = re.search(r"ios-app://\d+/item/(\w+)/(\d+)", txt)
    if findInfo is None:
        raise ValueError(f"Failed to establish listing type from `{txt}`")

    
    tp, listting_id = findInfo.group(1), int(findInfo.group(2))
    if tp not in {"a", "b"}:
        raise ValueError(tp, url)

    return listting_id, tp

Mypy提出了一个问题:

Expression of type "tuple[int, str]" cannot be assigned to return type "Tuple[int, > Literal['a', 'b']]" Tuple entry 2 is incorrect type Type "str" cannot be assigned to type "Literal['a', 'b']" "str" cannot be assigned to type "Literal['a']" "str" cannot be assigned to type "Literal['b']"


Tags: to字符串txtiftypebeintlisting
1条回答
网友
1楼 · 发布于 2024-09-29 06:32:45

这不起作用的原因是,只有当x等于"a""b"且没有其他字符串see ^{} semantics here)时,x才是Literal["a", "b"]

我可以这样子类str

class CustomStr(str):
    def __eq__(self, other):
        return True

它等于一切,包括"a"。要验证这一点,请执行以下操作:

>>> "a" == CustomStr()
True

>>> CustomStr() == "a"
True

>>> "b" == CustomStr()
True

>>> CustomStr() == "b"
True

(因此,不管是调用CustomStr()__eq__还是文字字符串的__eq__,这个str最终都等于"a""b"

检查此自定义字符串是否等于"a"不足以使其成为Literal["a"],因为它也等于其他所有字符串,这违反了Literal语义

一般来说,能够对类型进行子类化并重写它们的方法(在mypy中是完全类型安全的)使得基于它们的方法的任何类型的缩小都非常困难。这适用于示例的{"a", "b"}集的__contains__/in方法,以及findInfo__eq__/==

假设您的示例可以进行类型检查,如果:

  1. re.search以某种方式指示mypy它返回一个类型恰好为str的对象,而不是潜在的子类

  2. 由于使用了set文字mypy推断__contains__返回True意味着__contains__的参数是==,其中有一个元素在set中(该元素也假定__hash__正确实现,同样mypy无法保证str的任意子类的re.search是提示返回的类型)

然而,mypy在您的示例中没有利用这两个事实

要使示例真正进行类型检查,可以将其更改为:

def _get_listing_info(txt: str) -> Tuple[int, Literal["a", "b"]]:
    findInfo = re.search(r"ios-app://\d+/item/(\w+)/(\d+)", txt)
    if findInfo is None:
        raise ValueError(f"Failed to establish listing type from `{txt}`")

    tp, listting_id = findInfo.group(1), int(findInfo.group(2))

    if tp == "a":
        return listting_id, "a"

    if tp == "b":
        return listting_id, "b"

    raise ValueError(tp, "abc")

它是100%安全的,因为我们知道re.search不能返回满足这些==CustomStr,但最终不是"a"

相关问题 更多 >