python 3.5+中的类型提示克隆函数

时间:2017-06-29 23:35:04

标签: python python-3.x python-3.5 type-hinting

说我有以下课程:

class Parent:
    def clone_self(self) -> 'Parent':
        clone = self.__class__()
        # Initialize clone here.
        return clone

    def clone_with_class(self, klass: Type['Parent']) -> 'Parent':
        clone = klass()
        # Initialize clone here.
        return clone

class Child(Parent):
    def child_method(self) -> None:
        pass

有什么方法可以让这些类型更具体?我希望能够说出这样的话:

child = Child()
clone = child.clone_self()
clone.child_method()

clone = child.clone_with_class(Child)
clone.child_method()

但是,如上所述,这不会传递类型检查,因为克隆被认为是Parent而不是Child

我尝试使用TypeVar,但这似乎没有用 - 至少在PyCharm中是因为它然后抱怨当我尝试调用构造函数时该类型不可调用,可能是因为它涉及向前引用和PyCharm变得混乱。

Entity = TypeVar('Entity', bound='Parent')
class Parent:
    def clone_self(self) -> ???:
        clone = self.__class__()
        # initialize clone here
        return clone

    def clone_with_class(self, klass: Type[Entity]) -> Entity:
        clone = klass()
        # initialize clone here
        return clone

clone_with_class的此解决方案是否正确?也许PyCharm抱怨是错误的?否则,需要做些什么来修复上面的代码?

应该是TypeVar('Entity', bound='Parent')还是TypeVar('Entity', 'Parent')

我发现的另一个解决方案虽然看起来有些难看但是插入断言:

child = Child()
parent = Parent()

clone = child.clone_self()
clone.child_method()  # should work

clone = child.clone_with_class(Child)
clone.child_method()  # should work

clone = parent.clone_with_class(Child)
clone.child_method()  # should work

clone2 = parent.clone_self()
clone2.child_method()  # should be an error

clone2 = parent.clone_with_class(Parent)
clone2.child_method()  # should be an error

clone2 = child.clone_with_class(Parent)
clone2.child_method()  # Should be an error

一旦我对正确的内容有了一个很好的理解,我就可以在PyCharm上提出错误的错误提示。

根据建议的答案,mypy尝试了一下:

from typing import TypeVar, Type

Entity = TypeVar('Entity', bound='Parent')
class Parent:
    def clone_self(self: Entity) -> Entity:
        clone = type(self)()
        # initialize clone here
        return clone

    def clone_with_class(self, klass: Type[Entity]) -> Entity:
        clone = klass()
        # initialize clone here
        return clone

class Child(Parent):
    def child_method(self) -> None:
        print("Calling child method")

child = Child()
parent = Parent()

clone = child.clone_self()
clone.child_method()  # should work

clone = child.clone_with_class(Child)
clone.child_method()  # should work

clone = parent.clone_with_class(Child)
clone.child_method()  # should work

clone2 = parent.clone_self()
clone2.child_method()  # should be an error

clone2 = parent.clone_with_class(Parent)
clone2.child_method()  # should be an error

clone2 = child.clone_with_class(Parent)
clone2.child_method()  # Should be an error

我得到以下内容:

$ mypy --strict test.py
test.py:32: error: "Parent" has no attribute "child_method"
test.py:35: error: "Parent" has no attribute "child_method"
test.py:38: error: "Parent" has no attribute "child_method"

预计会出现这些错误。

1 个答案:

答案 0 :(得分:2)

我不知道PyCharm目前是否接受这个,但以下代码适用于mypy:

from typing import TypeVar, Type

Entity = TypeVar('Entity', bound='Parent')
class Parent:
    def clone_self(self: Entity) -> Entity:
        clone = self.__class__()
        # initialize clone here
        return clone

    def clone_with_class(self, klass: Type[Entity]) -> Entity:
        clone = klass()
        # initialize clone here
        return clone

class Child(Parent):
    def child_method(self) -> None:
        print("Calling child method")

child = Child()
clone = child.clone_self()
clone.child_method()

clone = child.clone_with_class(Child)
clone.child_method()

请注意,我为self提供了clone_self特定类型 - 这样我们就可以根据需要更准确地输入返回类型。您可以在此处详细了解如何使用通用自我:http://mypy.readthedocs.io/en/stable/generics.html#generic-methods-and-generic-self

您的clone_with_class方法中也有一个错误,可能会让您感到困惑 - 您忘记包含self参数。