为什么 Mypy 在 __init__ 中分配已在类主体中进行类型提示的属性时不给出键入错误?

sed*_*idw 6 python mypy

这是我的示例 python 文件

class Person:
    name: str
    age: int

    def __init__(self, name, age):
        self.name = name
        self.age = age


p = Person(5, 5)
Run Code Online (Sandbox Code Playgroud)

但是当我运行时mypy test.py我得到以下输出

$ mypy test.py                     
Success: no issues found in 1 source file
Run Code Online (Sandbox Code Playgroud)

难道它不应该抱怨它正在尝试分配5name变量并且我已经表明它应该是类型str

Mis*_*agi 5

TLDR:注释初始化器,而不是字段:

class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

p = Person(5, 5)  #  Argument 1 to "Person" has incompatible type "int"; expected "str"
Run Code Online (Sandbox Code Playgroud)

请参阅此样式的mypy 类基础知识。


这里有两个问题:

  • MyPy 不会检查完全未注释的函数/方法:

    MyPy:函数签名和动态与静态类型

    没有类型注释的函数被认为是由 mypy 动态类型化的:

    def greeting(name):
        return 'Hello ' + name
    
    Run Code Online (Sandbox Code Playgroud)

    默认情况下,mypy 不会对动态类型函数进行类型检查。这意味着,除了少数例外,mypy 不会报告常规未注释 Python 的任何错误。

    如果您想消除此类问题,请使用标志--disallow-untyped-defs--check-untyped-defs

  • 未注释的参数默认为Any

    class Person:
        name: str
        age: int
    
        def __init__(self, name, age: int) -> None:
            self.name = name
            reveal_type(name)       # Revealed type is 'Any'
    
            reveal_type(self.name)  # Revealed type is 'builtins.str'
            self.age = age
    
    Run Code Online (Sandbox Code Playgroud)

    即使您__init__被选中,它的参数也会采用并提供始终兼容的Any类型。

要直接解决这两个问题,请注释初始化参数而不是类槽(如果两者一致)。仅注释类槽(如果__init__是自动生成的),例如通过dataclasses.dataclasstyping.NamedTuple,或者如果推理不够精确则另外注释它们。