可选检查的错误代码(Error codes for optional checks)

本节记录了 mypy 仅在启用某些选项时生成的各种错误代码。有关错误代码及其配置的一般文档,请参阅 错误代码(Error codes)默认启用的错误代码(Error codes enabled by default) 记录了默认启用的错误代码。

备注

本节中的示例使用 inline configuration 指定 mypy 选项。您还可以通过使用 configuration filecommand-line options 设置相同的选项。

检查类型参数是否存在 [type-arg]

如果您使用 --disallow-any-generics,mypy 要求每个泛型类型为每个类型参数提供值。例如,类型 listdict 将被拒绝。您应该使用像 list[int]dict[str, int] 这样的类型。任何省略的泛型类型参数都会获得隐式的 Any 值。类型 list 相当于 list[Any],等等。

示例:

# mypy: disallow-any-generics

# 错误: 泛型类型 "list" 缺少类型参数  [type-arg]
def remove_dups(items: list) -> list:
    ...

检查每个函数是否有注释 [no-untyped-def]

如果您使用 --disallow-untyped-defs,mypy 要求所有函数都有注释(可以是 Python 3 注释或类型注解)。

示例:

# mypy: disallow-untyped-defs

def inc(x):  # 错误: 函数缺少类型注解  [no-untyped-def]
    return x + 1

def inc_ok(x: int) -> int:  # OK
    return x + 1

class Counter:
     # 错误: 函数缺少类型注解  [no-untyped-def]
     def __init__(self):
         self.value = 0

class CounterOk:
     # OK: 如果 "__init__" 不接受任何参数,则需要显式的 "-> None"
     def __init__(self) -> None:
         self.value = 0

检查类型转换是否冗余 [redundant-cast]

如果您使用 --warn-redundant-casts,当类型转换的源类型与目标类型相同时,mypy 将生成错误。

示例:

# mypy: warn-redundant-casts

from typing import cast

Count = int

def example(x: Count) -> int:
    # 错误: 转换为 "int" 的操作是冗余的  [redundant-cast]
    return cast(int, x)

检查方法是否具有冗余的 Self 注解 [redundant-self]

如果方法在返回类型或非 self 参数的类型中使用了 Self 类型,则不需要显式注解 self 参数。这种注解在 PEP 673 中是允许的,但属于冗余。如果启用此错误代码,mypy 将生成错误,如果存在冗余的 Self 类型。

示例:

# mypy: enable-error-code="redundant-self"

from typing import Self

class C:
    # 错误: 第一个方法参数的 "Self" 注解冗余
    def copy(self: Self) -> Self:
        return type(self)()

检查比较是否重叠 [comparison-overlap]

如果您使用 --strict-equality,mypy 将生成错误,如果它认为比较操作总是为真或为假。这些通常是错误。有时 mypy 可能过于严格,比较实际上是有用的。您可以仅在特定行上使用 # type: ignore[comparison-overlap] 来忽略该问题,而不是在所有地方禁用严格相等检查。

示例:

# mypy: strict-equality

def is_magic(x: bytes) -> bool:
    # 错误: 非重叠的相等检查(左操作数类型: "bytes",
    #        右操作数类型: "str")  [comparison-overlap]
    return x == 'magic'

我们可以通过将字符串字面量更改为字节字面量来修复错误:

# mypy: strict-equality

def is_magic(x: bytes) -> bool:
    return x == b'magic'  # OK

检查未注解的函数未被调用 [no-untyped-call]

如果您使用 --disallow-untyped-calls,mypy 在您在注解函数中调用未注解的函数时生成错误。

示例:

# mypy: disallow-untyped-calls

def do_it() -> None:
    # 错误: 在类型上下文中调用未注解的函数 "bad"  [no-untyped-call]
    bad()

def bad():
    ...

检查函数不返回 Any 值 [no-any-return]

如果您使用 --warn-return-any,mypy 将生成错误,如果您在注解为返回非 Any 值的函数中返回 Any 类型的值。

示例:

# mypy: warn-return-any

def fields(s):
     return s.split(',')

def first_field(x: str) -> str:
    # 错误: 从声明为返回 "str" 的函数返回 Any  [no-any-return]
    return fields(x)[0]

检查由于缺少导入而没有 Any 组件的类型 [no-any-unimported]

如果您使用 --disallow-any-unimported,mypy 如果类型的某个组件变为 Any,因为 mypy 无法解析导入,将生成错误。这些“隐形”的 Any 类型可能会令人惊讶,并意外导致不精确的类型检查。

在此示例中,我们假设 mypy 无法找到模块 animals,这意味着 Cat 在类型注解中回退为 Any

# mypy: disallow-any-unimported

from animals import Cat  # type: ignore

# 错误: "feed" 的参数 1 由于未跟随的导入而变为 "Any"  [no-any-unimported]
def feed(cat: Cat) -> None:
    ...

检查语句或表达式是否不可达 [unreachable]

如果您使用 --warn-unreachable,mypy 如果它认为某个语句或表达式将永远不会被执行,则会生成错误。在大多数情况下,这通常是由于不正确的控制流或条件检查意外总是为真或为假。

# mypy: warn-unreachable

def example(x: int) -> None:
    # 错误: "or" 的右操作数永远不会被评估  [unreachable]
    assert isinstance(x, int) or x == 'unused'

    return
    # 错误: 语句不可达  [unreachable]
    print('unreachable')

检查导入或使用的特性是否已弃用 [deprecated]

默认情况下,如果您的代码通过 from mod import depr 语句显式导入了已弃用的特性,或以其他方式使用了已弃用的特性或在本地定义了已弃用的特性,mypy 会生成一个通知。当特性被 warnings.deprecated 装饰时,视为已弃用,如 PEP 702 中所述。您可以通过 # type: ignore[deprecated] 来静默单个通知,或通过 --disable-error-code=deprecated 完全关闭此检查。使用 --report-deprecated-as-error 选项以获得更严格的检查,将所有此类通知转换为错误。

备注

warnings 模块自 Python 3.13 起提供 @deprecated 装饰器。 若要在旧版本的 Python 中使用,请从 typing_extensions 导入它。

示例:

# mypy: report-deprecated-as-error

# 错误: abc.abstractproperty 已弃用:已弃用,请使用 'property' 和 'abstractmethod' 替代
from abc import abstractproperty

from typing_extensions import deprecated

@deprecated("use new_function")
def old_function() -> None:
    print("I am old")

# 错误: __main__.old_function 已弃用:使用 new_function
old_function()
old_function()  # type: ignore[deprecated]

检查表达式是否冗余 [redundant-expr]

如果您使用 --enable-error-code redundant-expr,mypy 将生成错误,如果它认为某个表达式是冗余的。

# mypy: enable-error-code="redundant-expr"

def example(x: int) -> None:
    # 错误: "and" 的左操作数总是为真  [redundant-expr]
    if isinstance(x, int) and x > 0:
        pass

    # 错误: 如果条件总是为真  [redundant-expr]
    1 if isinstance(x, int) else 0

    # 错误: 生成式中的条件总是为真  [redundant-expr]
    [i for i in range(x) if isinstance(i, int)]

警告有关仅在某些执行路径中定义的变量 [possibly-undefined]

如果您使用 --enable-error-code possibly-undefined,mypy 将生成错误,如果它无法验证变量在所有执行路径中都会被定义。这包括变量定义出现在循环中、条件分支中、异常处理器中等情况。例如:

# mypy: enable-error-code="possibly-undefined"

from collections.abc import Iterable

def test(values: Iterable[int], flag: bool) -> None:
    if flag:
        a = 1
    z = a + 1  # 错误: 名称 "a" 可能未定义 [possibly-undefined]

    for v in values:
        b = v
    z = b + 1  # 错误: 名称 "b" 可能未定义 [possibly-undefined]

检查表达式在布尔上下文中不隐式为真 [truthy-bool]

当布尔上下文中的表达式类型未实现 __bool____len__ 时发出警告。除非这些中的一个由子类型实现,否则表达式将始终被视为真,并且条件中可能存在错误。

作为例外,object 类型在布尔上下文中是允许的。 在布尔上下文中使用可迭代值有单独的错误代码(见下文)。

# mypy: enable-error-code="truthy-bool"

class Foo:
    pass
foo = Foo()
# 错误: "foo" 的类型为 "Foo",未实现 __bool__ 或 __len__,因此在布尔上下文中可能始终为真
if foo:
     ...

检查可迭代对象在布尔上下文中不隐式为真 [truthy-iterable]

如果类型为 Iterable 的值用作布尔条件,则生成错误,因为 Iterable 并未实现 __len____bool__

示例:

from collections.abc import Iterable

def transform(items: Iterable[int]) -> list[int]:
    # 错误: "items" 的类型为 "Iterable[int]",在布尔上下文中可能始终为真。考虑使用 "Collection[int]" 替代。  [truthy-iterable]
    if not items:
        return [42]
    return [x + 1 for x in items]

如果 transform 被调用时传入 Generator 参数,例如 int(x) for x in [],则该函数将不会返回 [42],与预期可能不同。当然,transform 可能仅在 list 或其他容器对象上调用,并且 if not items 检查实际上是有效的。如果是这种情况,建议将 items 注解为 Collection[int] 而不是 Iterable[int]

检查 # type: ignore 是否包含错误代码 [ignore-without-code]

# type: ignore 注释未指定任何错误代码时发出警告。这可以明确忽略的意图,并确保仅静默预期的错误。

示例:

# mypy: enable-error-code="ignore-without-code"

class Foo:
    def __init__(self, name: str) -> None:
        self.name = name

f = Foo('foo')

# 这行有一个拼写错误,mypy 无法处理,因为:
# - 预期错误 'assignment',以及
# - 意外错误 'attr-defined'
# 都被静默。
# 错误: "type: ignore" 注释没有错误代码(考虑使用 "type: ignore[attr-defined]")
f.nme = 42  # type: ignore

# 这一行正确地警告了属性名称中的拼写错误
# 错误: "Foo" 没有属性 "nme"; 也许是 "name"?
f.nme = 42  # type: ignore[assignment]

检查可等待返回值是否被使用 [unused-awaitable]

如果您使用 --enable-error-code unused-awaitable,mypy 将生成错误,如果您不使用一个定义了 __await__ 的返回值。

示例:

# mypy: enable-error-code="unused-awaitable"

import asyncio

async def f() -> int: ...

async def g() -> None:
    # 错误: 类型 "Task[int]" 的值必须被使用
    #        您是否缺少 await?
    asyncio.create_task(f())

您可以将值赋给一个临时的、未使用的变量来静默错误:

async def g() -> None:
    _ = asyncio.create_task(f())  # 没有错误

检查 # type: ignore 注释是否被使用 [unused-ignore]

如果您使用 --enable-error-code unused-ignore,或 --warn-unused-ignores,mypy 将生成错误,如果您没有使用 # type: ignore 注释,即如果有注释,但这一行上不会由 mypy 生成任何错误。

示例:

# 使用 "mypy --warn-unused-ignores ..."

def add(a: int, b: int) -> int:
    # 错误: 未使用的 "type: ignore" 注释
    return a + b  # type: ignore

请注意,由于此注释的特定性质,唯一可以选择性静默它的方法是显式包含错误代码。还请注意,如果由于代码静态不可达(例如由于平台或版本检查),未使用 # type: ignore ,则不会显示此错误。

示例:

# 使用 "mypy --warn-unused-ignores ..."

import sys

try:
    # "[unused-ignore]" 是必需的,以便在 Python 3.8 和 3.9 上进行干净的 mypy 运行
    # 此模块在这两个版本中均已添加
    import graphlib  # type: ignore[import,unused-ignore]
except ImportError:
    pass

if sys.version_info >= (3, 9):
    # 以下内容在 Python 3.8 或 3.9 上都不会生成错误
    42 + "testing..."  # type: ignore

检查在重写基类方法时是否使用 @override [explicit-override]

如果您使用 --enable-error-code explicit-override ,mypy 将生成错误,如果您在重写基类方法时未使用 @override 装饰器。重写 __init____new__ 时不会发出错误。请参见 PEP 698

备注

从 Python 3.12 开始,可以从 typing 导入 @override。 若要在旧版本的 Python 中使用,请从 typing_extensions 导入它。

示例:

# mypy: enable-error-code="explicit-override"

from typing import override

class Parent:
    def f(self, x: int) -> None:
        pass

    def g(self, y: int) -> None:
        pass


class Child(Parent):
    def f(self, x: int) -> None:  # 错误: 缺少 @override 装饰器
        pass

    @override
    def g(self, y: int) -> None:
        pass

检查可变属性的重写是否安全 [mutable-override]

mutable-override 将启用对可变属性不安全重写的检查。由于历史原因,并且因为这是 Python 中相对常见的模式,因此默认情况下未启用此检查。下面的示例是不安全的,当启用此错误代码时将被标记:

from typing import Any

class C:
    x: float
    y: float
    z: float

class D(C):
    x: int  # 错误: 可变属性的协变重写
            # (基类 "C" 定义的类型为 "float",
            # 表达式的类型为 "int")[mutable-override]
    y: float  # 正确
    z: Any  # 正确

def f(c: C) -> None:
    c.x = 1.1
d = D()
f(d)
d.x >> 1  # 这将在运行时崩溃,因为 d.x 现在是 float,而不是 int

检查 reveal_type 是否从 typing 或 typing_extensions 导入 [unimported-reveal]

Mypy 以前将 reveal_type 作为一种特殊的内置函数,仅在类型检查期间存在。在运行时,它会以预期的 NameError 失败,这可能在生产中造成实际问题,而被 mypy 隐藏。

但是,在 Python3.11 中添加了 typing.reveal_type()typing_extensions 将此辅助功能移植到所有支持的 Python 版本中。

现在用户可以实际导入 reveal_type 来确保运行时代码安全。

备注

从 Python 3.11 开始,可以从 typing 导入 reveal_type。 若要在旧版本的 Python 中使用,请从 typing_extensions 导入它。

# mypy: enable-error-code="unimported-reveal"

x = 1
reveal_type(x)  # 注意: 显示的类型是 "builtins.int" \
                # 错误: 名称 "reveal_type" 未定义

正确用法:

# mypy: enable-error-code="unimported-reveal"
from typing import reveal_type   # 或者 `typing_extensions`

x = 1
# 这不会引发错误:
reveal_type(x)  # 注意: 显示的类型是 "builtins.int"

当启用此代码时,使用 reveal_locals 始终是错误的,因为无法导入它。

检查 TypeIs 是否缩小类型 [narrowed-type-not-subtype]

PEP 742 要求在使用 TypeIs 时,缩小的类型必须是原始类型的子类型:

from typing_extensions import TypeIs

def f(x: int) -> TypeIs[str]:  # 错误,str 不是 int 的子类型
    ...

def g(x: object) -> TypeIs[str]:  # 正确
    ...