如何在 Python 元类中键入 __new__ 方法让 mypy 开心

Enr*_*aud 6 python metaclass typing

我正在尝试__new__在 Python 的元类中键入该方法,以便它使 mypy 满意。代码将是这样的(取自pep-3115 - “Python 3000 中的元类”并精简了一点):

from __future__ import annotations

from typing import Type


# The metaclass
class MetaClass(type):

    # The metaclass invocation
    def __new__(cls: Type[type], name: str, bases: tuple, classdict: dict) -> type:
        result = type.__new__(cls, name, bases, classdict)
        print('in __new__')
        return result


class MyClass(metaclass=MetaClass):
    pass
Run Code Online (Sandbox Code Playgroud)

有了这个,mypy 抱怨说,Incompatible return type for "__new__" (returns "type", but must return a subtype of "MetaClass"),指着线def __new__

我也试过:

def __new__(cls: Type[MetaClass], name: str, bases: tuple, classdict: dict) -> MetaClass:
Run Code Online (Sandbox Code Playgroud)

然后mypy抱怨(约return result行)Incompatible return value type (got "type", expected "MetaClass")

我也尝试过使用 var( TSubMetaclass = TypeVar('TSubMetaclass', bound='MetaClass'))类型,结果与使用MetaClass.

使用super().__new__而不是type.__new__给出了类似的结果。

什么是正确的方法呢?

che*_*ner 5

首先,返回类型是MetaClass,不是type。其次,您需要显式转换返回值,因为type.__new__不知道它正在返回MetaClass. (它的具体返回类型由它的第一个参数决定,它不是静态已知的。)

from __future__ import annotations

from typing import Type, cast


# The metaclass
class MetaClass(type):

    # The metaclass invocation
    def __new__(cls: Type[type], name: str, bases: tuple, classdict: dict) -> MetaClass:
        result = type.__new__(cls, name, bases, classdict)
        print('in __new__')
        return cast(MetaClass, result)


class MyClass(metaclass=MetaClass):
    pass
Run Code Online (Sandbox Code Playgroud)

使用时super,需要调整cls参数的静态类型。

class MetaClass(type):

    # The metaclass invocation
    def __new__(cls: Type[MetaClass], name: str, bases: tuple, classdict: dict) -> MetaClass:
        result = super().__new__(name, bases, classdict)
        print('in __new__')
        return cast(MetaClass, result)
Run Code Online (Sandbox Code Playgroud)

  • 嗯,是的,如果我们将 `cls` 注释更改为 `Type[MetaClass]` *并且*我们转换结果,我们可以使用 `super()` 并且它会让 mypy 满意......我真的希望有一个解决方案 w/ o 选角,但现在我会接受你的答案:) (2认同)