mypy可以根据当前对象的类型选择方法返回类型吗?

时间:2019-08-10 23:02:40

标签: python-3.x mypy

在下面的代码中,在A的实例上调用clone()方法将返回类型A的实例,在B的实例上调用它将返回B的实例,依此类推。目的是创建一个新实例,该实例与当前实例相同,但内部生成的主键有所不同,因此可以从那里进行编辑,并安全地另存为新项目。

???是某种类型限定符,但我不确定什么是正确的选择。

class A:
    def clone(self) -> ???:
        cls = self.__class__
        result = cls.__new__(cls)
        for k, v in self.__dict__.items():
            setattr(result, k, deepcopy(v))
        result.id = self.__generate_id()
        return result

class B(A):
   def do_b(self):
       pass

我目前将???替换为'A'。这行得通,但是如果我要克隆类型B的对象,那么如果要在其上调用B特定的方法,则必须强制转换返回值:

b = B().clone()
b.do_b()  # type error!

b = cast(B, B().clone())
b.do_b()  # OK

但是,可以保证在类型B的对象上调用.clone()将返回另一个类型B的对象,这对我来说有点像Java。有什么基于通用的方式可以告诉mypy该方法返回类型为__class__的对象,无论该类是什么?

1 个答案:

答案 0 :(得分:2)

您可以通过使用generic methods with a generic self来做到这一点-基本上,将self变量注释为通用变量:

from typing import TypeVar

T = TypeVar('T', bound='A')

class A:
    def __generate_id(self) -> int:
        return 0

    def clone(self: T) -> T: 
        cls = self.__class__
        result = cls.__new__(cls)
        for k, v in self.__dict__.items():
            setattr(result, k, deepcopy(v))
        result.id = self.__generate_id()
        return result

class B(A):
    def do_b(self):
        pass

reveal_type(A().clone())  # Revealed type is A
reveal_type(B().clone())  # Revealed type is B

基本上,当我们调用clone()时,当mypy尝试对克隆的调用进行类型检查时,T typevar将绑定到当前实例的类型。最终使返回类型与实例的类型匹配,无论它是什么。

您可能想知道为什么我将T的上限设置为A。这是因为行self.__generate_id()。基本上,为了对该行进行类型检查,self实际上不能是任何类型:它必须是AA的某个子类。边界编码了这一要求。