【问题标题】:How to typehint regular expression within lambda with python mypy如何使用 python mypy 在 lambda 中键入提示正则表达式
【发布时间】:2021-11-26 11:42:53
【问题描述】:

例子:

import re

x = [
    "info_2_mation_1_thing",
    "info_2_mation_5_thing",
    "info_2_mation_2_thing",
    "info_2_mation_3_thing",
    "info_2_mation_13_thing",
    "info_2_mation_4_thing",
]
sorted(x, key=lambda x: int(re.match(r"info_\d_mation_(\d+)_thing", x).group(1)))

给出错误:

$ mypy test.py
test.py:13: error: Item "None" of "Optional[Match[Any]]" has no attribute "group"  [union-attr]
test.py:13: error: Item "None" of "Optional[Match[str]]" has no attribute "group"  [union-attr]

我不确定我应该如何输入这个?

【问题讨论】:

    标签: python type-hinting mypy re python-typing


    【解决方案1】:

    Mypy 实际上在这里指出了一个真正的问题:正则表达式可能不匹配,如果不匹配,尝试访问该组确实会抛出。 (即re.Match 将返回None)。

    这是两种情况之一:要么你知道那些是唯一会调用正则表达式的字符串,在这种情况下你比 mypy 知道的更多,你只需输入一个# type: ignore 告诉 mypy 你真的最了解;或者你打算在真实世界的数据上运行它,在这种情况下 mypy 发现了一个问题,你想要这样的东西:

    try:
        sorted(x, key=lambda x: int(re.match(r"info_\d_mation_(\d+)_thing", x).group(1))) # type: ignore
    except AttributeError:
        ...
    

    【讨论】:

    • 运行 mypy test.py 返回相同的错误,放入 try / except 似乎并没有通知 mypy
    • 是的,抱歉,您需要告诉 mypy 在 try 块中使用评论保持沉默
    【解决方案2】:

    正如@2e0byo 在their answer 中所说,这里的问题是,一般来说,re.match 可能会返回 Match 对象,或者它可能会返回 None,而 Mypy 会就此向您发出警告。

    如果您知道(出于某种原因)此模式将始终匹配,您可以通过添加 cast(文档 here)来使 Mypy 静音:

    import re
    from typing import cast
    
    x = [
        "info_2_mation_1_thing",
        "info_2_mation_5_thing",
        "info_2_mation_2_thing",
        "info_2_mation_3_thing",
        "info_2_mation_13_thing",
        "info_2_mation_4_thing",
    ]
    
    result = sorted(
        x,
        key=lambda x: int(cast(re.Match[str], re.match(r"info_\d_mation_(\d+)_thing", x)).group(1))
    )
    

    或者,您可以通过添加 # type: ignore 评论使 MyPy 静音。 (最好只为某一行静默特定的 mypy 错误,而不是静默某一行的所有 mypy 错误。这与您永远不应在 try/@987654332 中使用裸 except 的原则相同@ 块。您可以使用 --show-error-codes 选项找出 Mypy 引发的特定类型的错误。在这种情况下,它是 [union-attr] 代码,因此我们使用 # type: ignore[union-attr] 仅将那些特定错误静音。)

    import re
    
    x = [
        "info_2_mation_1_thing",
        "info_2_mation_5_thing",
        "info_2_mation_2_thing",
        "info_2_mation_3_thing",
        "info_2_mation_13_thing",
        "info_2_mation_4_thing",
    ]
    
    result = sorted(
        x,
        key=lambda x: int(re.match(r"info_\d_mation_(\d+)_thing", x).group(1)) # type: ignore[union-attr]
    )
    

    如果您不知道该模式将始终匹配,那么 Mypy 将引发一个有效错误。在这种情况下,为了满足 MyPy,您需要引入某种测试检查 re.match 的结果不是 None。由于Match 对象总是真实的,因此在这里测试来自re.match 的返回值的真实性是安全且惯用的,即使通常我们会使用is 测试None 的存在。在这个 sn-p 中,我使用了 Python 3.8 中引入的walrus operator

    import re
    
    x = [
        "info_2_mation_1_thing",
        "info_2_mation_5_thing",
        "info_2_mation_2_thing",
        "info_2_mation_3_thing",
        "info_2_mation_13_thing",
        "info_2_mation_4_thing",
    ]
    
    DEFAULT = -1
    
    result = sorted(
        x, 
        key=lambda x: int(m.group(1)) if (m := re.match(r"info_\d_mation_(\d+)_thing", x)) else DEFAULT
    )
    

    所有这三个解决方案都会导致相当长的lambda 函数。缩写这些函数的一种方法(并使您的代码稍微更有效作为一个愉快的副作用)是预编译您的正则表达式模式。我上面给出的第三个解决方案将被重构为:

    import re
    
    x = [
        "info_2_mation_1_thing",
        "info_2_mation_5_thing",
        "info_2_mation_2_thing",
        "info_2_mation_3_thing",
        "info_2_mation_13_thing",
        "info_2_mation_4_thing",
    ]
    
    DEFAULT = -1
    PATTERN = re.compile(r"info_\d_mation_(\d+)_thing")
    
    result = sorted(
        x, 
        key=lambda x: int(m.group(1)) if (m := PATTERN.match(x)) else DEFAULT
    )
    

    【讨论】:

    • 不错!不知道typing.cast,我会记住的
    猜你喜欢
    • 2016-11-28
    • 2020-04-13
    • 1970-01-01
    • 2019-11-11
    • 2021-06-27
    • 2021-05-16
    • 2021-07-30
    • 1970-01-01
    相关资源
    最近更新 更多