为什么 Mypy 在 __init__ 中分配已在 class 正文中提示类型的属性时不给出键入错误?

Why is Mypy not giving a typing error when assigning attributes in __init__ that have been type hinted in the class body?

这是我的示例 python 文件

class Person:
    name: str
    age: int

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


p = Person(5, 5)

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

$ mypy test.py                     
Success: no issues found in 1 source file

它不应该抱怨它试图将 5 分配给 name 变量并且我已经指出它应该是 str

类型

使用 reveal_type 可以为您提供有用的提示,说明为什么会发生这种情况:

class Person:
    name: str
    age: int

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


p = Person(5, 5)

Output:

test_mypy1.py:8: note: Revealed type is 'Any'
test_mypy1.py:8: note: 'reveal_type' always outputs 'Any' in unchecked functions

所以原因是 Mypy 默认情况下根本不检查 __init__ 方法的类型错误,因为你没有类型注释它。

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"

查看此样式的 mypy Class Basics


这里有两个问题:

  • MyPy 未检查完全未注释的 functions/methods:

    MyPy: Function signatures and dynamic vs static typing

    A function without type annotations is considered to be dynamically typed by mypy:

    def greeting(name):
        return 'Hello ' + name
    

    By default, mypy will not type check dynamically typed functions. This means that with a few exceptions, mypy will not report any errors with regular unannotated Python.

    如果您想消除此类漏掉的问题,请使用标志 --disallow-untyped-defs or --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
    

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

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