如何将 mypy 推断循环变量修复为 "object"?

How to fix mypy inferring loop variable as "object"?

我做了这个简单的函数,我想用 mypy 和 皮林特。它只是解析一个字符串并将其转换为适当的 类型。

import re
from typing import Any, Callable
    
def parse_constant(constant: str) -> Any:
    for reg, get_val in [
            (re.compile(r'\'(.*)\''), str),
            (re.compile(r'true', re.IGNORECASE), lambda _: True),
            (re.compile(r'false', re.IGNORECASE), lambda _: False),
            (re.compile(r'([0-9]+)'), int),
            (re.compile(r'([0-9]+\.[0-9]+)'), float)
    ]:
        match = reg.fullmatch(constant)
        if match is not None:
            if len(match.groups()) == 0:
                val = None
            else:
                val = match.groups()[0]
            return get_val(val)
    return None

它工作正常但 mypy 抱怨:我得到 error: "object" not callable 在第 18 行 (return get_val(val)).

现在,如果我将 str 替换为 lambda x: str(x) mypy 很高兴,但 pylint 抱怨 Lambda may not be necessary.

解决这个问题的正确方法是什么?

问题是 MyPy 必须从 CallableType 的混合中推断出 get_val。在这种情况下, 显式注释类型以避免过于宽泛的推断。

for循环内,只能注解循环变量。通过把iterable移到循环外,可以注解:

import re
from typing import Any, Callable, Pattern, List, Tuple

cases: List[Tuple[Pattern[str], Callable]] = [
    (re.compile(r'\'(.*)\''), str),
    (re.compile(r'true', re.IGNORECASE), lambda _: True),
    (re.compile(r'false', re.IGNORECASE), lambda _: False),
    (re.compile(r'([0-9]+)'), int),
    (re.compile(r'([0-9]+\.[0-9]+)'), float)
]

def parse_constant(constant: str) -> Any:
    for reg, get_val in cases:
        match = reg.fullmatch(constant)
        if match is not None:
            if len(match.groups()) == 0:
                val = None
            else:
                val = match.groups()[0]
            return get_val(val)
    return None

将案例移到函数之外还有一个额外的好处,即它们只创建一次。这对于 re.compile 尤其重要,它现在编译一次,然后存储以供重复使用。