我使用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
语句之类的内容来本质上只是使mypy
静音而不增加功能,同时使代码的可读性降低。我也想避免到处评论# type: ignore
。
我尝试了猴子修补Element.find.__annotations__
,在我看来这将是一个很好的解决方案。但是由于它是内置函数,所以我无法做到这一点,而对Element
进行子类化又感觉又太多了。
有解决这个问题的好方法吗?
答案 0 :(得分:2)
我们可以编写一个实用程序函数,该函数在内部处理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 但是,如果您正在使用某些由用户提供的数据,我建议引发类似< / p>
if result is None:
raise LookupError('No tag "{tag}" found '
'in element "{element}".'
.format(tag=tag,
element=element))
我使用类型注释,因为它有助于IDE,并且在读取API时还节省了大量时间,但是我不是mypy用户,因为我不喜欢这种情况下检查所有内容的想法:函数用户传递垃圾,这是他的错,我们应该让他这样做,而不是写“ {您有一个类型的并集,而不用其中的一些来处理案例”” EAFP after all。
答案 1 :(得分:1)
Mypy不使用__annotations__
,它是运行时构造。 Mypy的分析是完全静态的。
“内置”类型(又称标准库中的类型)来自typeshed。如果您希望出于自己的目的修改这些类型,则可以这样做(尽管我强烈建议不要将其作为解决问题的方法)。要将自定义排版应用于mypy,您可以执行mypy --custom-typeshed-dir=/path/to/my/typeshed ...
,mypy将使用修改后的排版。
一个更符合人体工程学的解决方案将按照Azat的建议进行,并编写一个将类型变窄移动到实用程序功能的包装程序,以使本地可读性不会受到影响,并保持类型安全。
答案 2 :(得分:1)
我认为在这里,您可以选择三种不同的选择。
第二个选项是配置mypy并放松其处理“ None”类型的值的方式。当前,mypy会将“ None”和“ Element”视为两种截然不同的类型:如果您的值是“ None”,则它不能是“ Element”,反之亦然。您实际上可以通过给mypy提供--no-strict-optional
标志来弱化它,这将使mypy将类型'None'的值视为 all 类型的成员。
或者换句话说,如果您熟悉Java之类的语言,则可以这样做:
String myString = null;
将--no-strict-optional
标志传递给mypy将使其开始接受上述代码。
这显然意味着您的代码的类型安全性将降低:mypy不再能够检测潜在的“空指针异常”。为了缓解这种情况,您可以尝试通过创建mypy config file来禁用严格可选的 local ,而不是 global 。
简而言之,您将创建一个大致如下所示的配置文件:
[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
第三个选项是完全停止对XML解析代码使用静态类型:将root
变量转换为“ Any”类型,然后转到城镇。然后,当您从XML收集有用的数据时,请执行所有必要的运行时检查以验证数据并创建(typesafe!)对象以存储相关信息。 (当然,您可以在其余代码中继续使用静态类型)。
这里的观察结果是,任何运行时输入都将固有地是动态的:用户始终可以传递格式错误的XML,数据的结构可能不正确,等等。...检查此类问题的唯一真实方法是使用运行时检查:静态类型检查不会有太大帮助。因此,如果静态类型检查在代码的特定区域提供了最小值,那么为什么要在此处继续使用它呢?
当然,这种策略确实有几个缺点。特别是,mypy无法检测到对ElementTree API的公然滥用,您将需要对运行时检查进行认真检查,以确保不良数据不会进入代码的类型检查区域等。