【问题标题】:How to type the __new__ method in a Python metaclass so that mypy is happy如何在 Python 元类中键入 __new__ 方法以使 mypy 满意
【发布时间】:2020-11-13 04:42:47
【问题描述】:

我正在尝试在 Python 的元类中键入 __new__ 方法,以便让 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

这样,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:

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

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

使用super().__new__ 代替type.__new__ 得到了类似的结果。

正确的做法是什么?

【问题讨论】:

    标签: python metaclass typing


    【解决方案1】:

    首先,返回类型是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

    要使用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)

    【讨论】:

    • 这似乎是一个 hack - 例如,该签名会阻止您使用 super(),因为第二个参数不会是第一个参数的实例。好吧,老实说,cast 在 Python 输入中似乎通常是错误的,就像一个谎言。在我看来,如果我们从def __new__(cls: Type[MetaClass [...] 之类的东西开始,那么type.__new__(cls [...] 应该明白它正在返回MetaClass 的实例。因此,在接受您的回答之前,我将等着看是否有不使用 cast 的解决方案。谢谢!
    • 你没有错。我确实注意到super 的问题,这就是为什么我没有将它包含在答案中。
    • super 的问题可以通过将cls 的注解更改为Type[MetaClass] 来解决,但我认为如果您有元类层次结构,这有其自身的问题。 (不过,想到这么复杂的事情,我不寒而栗。)
    • 嗯,是的,如果我们将cls 注释更改为Type[MetaClass] 并且我们将结果转换,我们可以使用super(),这很高兴mypy...我真的希望有一个没有强制转换的解决方案,但现在我会接受你的回答:)
    • 我希望看到已修补的答案以包含正确的 super() 呼叫。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-16
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    • 2020-08-05
    • 2015-12-15
    • 2021-11-26
    相关资源
    最近更新 更多