代码之家  ›  专栏  ›  技术社区  ›  Arne

如何操作内置类型提示

  •  4
  • Arne  · 技术社区  · 6 年前

    我用 ElementTree 解析/构建一些稍微复杂但定义良好的XML文件,并使用 mypy 用于静态类型。我有 .find 到处都是这样的声明,导致了这样的事情:

    from xml.etree.ElementTree import Element
    ...
    root.find('tag_a').append(Element('tag_b'))
    
    # run mypy..
    -> type None from Optional[Element] has no attribute append
    

    这是有道理的,因为 find 找不到我给它的标签。但我知道它就在那儿,不想再加上 try..except assert 基本上只是沉默的陈述 梅皮 不添加功能,同时降低代码的可读性。我也不想评论 # type: ignore 到处都是。


    我试过修补猴子 Element.find.__annotations__ 我认为这是一个很好的解决办法。但是由于它是内置的,所以我不能这样做,并且子类化 Element 感觉又太多了。

    有什么好办法解决这个问题吗?

    3 回复  |  直到 6 年前
        1
  •  2
  •   Azat Ibrakov    6 年前

    我们可以编写一个内部处理的实用函数 None -找到个事例并引发异常/返回给定类型的一些虚拟值:

    from xml.etree.ElementTree import Element
    
    
    def find(element: Element,
             tag: str) -> Element:
        result = element.find(tag)
        assert result is not None, ('No tag "{tag}" found '
                                    'in element "{element}".'
                                    .format(tag=tag,
                                            element=element))
        return result
    

    断言的优点(与手动引发异常相比)是 can be disabled 但是 如果您使用用户数据提供的某些数据,我建议引发异常,例如

    if result is None:
        raise LookupError('No tag "{tag}" found '
                          'in element "{element}".'
                          .format(tag=tag,
                                  element=element))
    

    离题

    我使用类型注释是因为它有助于IDE,而且在读取API时也节省了很多时间,但我不是mypy用户,因为我不喜欢像这种情况下检查所有内容的想法:如果函数用户传递垃圾,那么这是他的错,我们应该让他这样做,而不是写一些东西。ng关于“你有一个类型的联合,而不是用其中的一些来处理案例”, EAFP after all .

        2
  •  1
  •   ethanhs    6 年前

    Mypy不使用 __annotations__ ,这是运行时构造。Mypy的分析是完全静态的。

    “builtin”类型(即标准库中的类型)源于 typeshed . 如果您希望为自己的目的修改这些类型,您可以(尽管我强烈反对将其作为解决您的问题的解决方案)。要在mypy中使用自定义排版,可以 mypy --custom-typeshed-dir=/path/to/my/typeshed ... 而mypy将使用你修改过的排版。

    一个更符合人体工程学的解决方案是按照azat的建议来做,并编写一个包装器,将类型缩小到实用函数,这样本地可读性就不会受到影响,并且您可以维护类型安全。

        3
  •  1
  •   Michael0x2a    6 年前

    我想这里有三种不同的选择。

    1. 第一种选择是 Azat Ibrakov's answer :创建一个助手方法,该方法在运行时显式执行“无”检查以满足mypy。这是最安全的类型选项。
    2. 第二个选项是配置mypy并松开它处理“none”类型值的方式。目前,mypy将“none”和“element”视为两种不同的类型:如果您的值为“none”,则它不能是“element”,反之亦然。你可以通过给Mypy --no-strict-optional 标志,它将使mypy将“none”类型的值视为 全部的 类型。

      或者换句话说,如果你熟悉像Java这样的语言,那么做这样的事情是合法的:

      String myString = null;
      

      通过 --无严格可选 标记为mypy将使它开始接受上述代码。

      这显然意味着代码的类型安全性会降低:mypy不再能够检测潜在的“空指针异常”。为了帮助缓解这种情况,您可以尝试禁用严格可选 本地 ,而不是 全球地 ,通过创建 mypy config file .

      简言之,您将创建一个大致如下的配置文件:

      [mypy]
      # Global options can go here. We'll leave this empty since we don't
      # want to change any of the defaults.
      
      [mypy-mycodebase.my.xml.processing.module]
      # We weaken mypy in *just* this module
      strict_optional = False
      
    3. 第三种选择是完全停止对XML解析代码使用静态类型:强制转换 root 变量的类型为“any”,然后进城。然后,在从XML收集有用数据时,执行任何必要的运行时检查以验证数据并创建(typesafe!)对象来存储相关信息。(当然,您可以继续对代码的其余部分使用静态类型)。

      这里的观察是,任何运行时输入都将具有内在的动态性:用户总是可以传递格式错误的XML,数据的结构可能不正确,等等。检查这些问题的唯一真正方法是使用运行时检查:静态类型检查没有太大帮助。所以,如果静态类型检查在代码的某个区域中提供了最小值,为什么还要在那里继续使用它呢?

      当然,这种策略有几个缺点。尤其是,mypy将无法检测到元素树API的明显错误使用,您需要相当努力地进行运行时检查,以确保坏数据不会蔓延到代码的类型检查区域,等等。