类型:声明类方法类装饰器

时间:2021-05-06 09:36:27

标签: python mypy class-method python-typing

mypy 将 @classmethod 的绑定语义融入其中(实际上从查看源代码我相信它是为 builtins.classmethod 硬编码的)所以这有效:

from __future__ import annotations
from typing import Any


class Foo:
    def __init__(self, x: Any) -> None:
        self.x = x

    @classmethod
    def create(cls, x: Any) -> Foo:
        return cls(x)


if __name__ == '__main__':
    foo = Foo.create(1)
    print(foo)
$ mypy a0.py
Success: no issues found in 1 source file

但是说,出于正当理由,我有一个装饰器,它在第一个参数上具有类似 classmethod 的绑定行为(期望它是一个类)。举一个近似 classmethod 本身的简单例子:

class myclassmethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, obj, cls):
        return partial(self.func, cls)


class Foo:
    def __init__(self, x: Any) -> None:
        self.x = x

    @myclassmethod
    def create(cls, x: Any) -> Foo:
        return cls(x)

不出所料,这不会结账; mypy 假定第一个参数绑定到一个 Foo 实例,并且在 cls(x) 的调用站点出错:

$ mypy a1.py
a1.py:20: error: "Foo" not callable

如果我尝试明确地给“cls”一个类型与实例方法的默认 bind_self 行为相矛盾:

class Foo:
    def __init__(self, x: Any) -> None:
        self.x = x

    @myclassmethod
    def create(cls: Type[Foo], x: Any) -> Foo:
        return cls(x)
$ mypy a2.py 
a2.py:19: error: The erased type of self "Type[a2.Foo]" is not a supertype of its class "a2.Foo"

我发现的唯一解决方法是不在签名中声明 cls 的类型,而是在调用站点进行转换:

class Foo:
    def __init__(self, x: Any) -> None:
        self.x = x

    @myclassmethod
    def create(cls, x: Any) -> Foo:
        return cast(Type[Foo], cls)(x)

就类型检查而言,这通过了,但有点不方便且容易出错,因为这必须由 @myclassmethod 的任何用户完成。

我看到 mypy 的 is_classmethod 有一个 bind_self 参数,但这实际上仅适用于使用 builtins.classmethod 修饰的函数的特定情况。子类化 classmethod 也无济于事(我猜 mypy 保守地假设 classmethod 的子类可能具有任意行为)。

我只是想知道是否有一种方法可以在不深入研究 mypy 内部结构的情况下显式覆盖作为装饰器参数的函数的自绑定行为。

0 个答案:

没有答案