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

Posted

技术标签:

【中文标题】python 3.5+中的类型提示克隆函数【英文标题】:type hint clone function in python 3.5+ 【发布时间】:2017-12-03 19:53:07 【问题描述】:

假设我有以下课程:

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】:

我不知道 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 的特定类型——这让我们可以根据需要更精确地键入返回类型。您可以在此处了解有关使用通用 self 的更多信息:http://mypy.readthedocs.io/en/stable/generics.html#generic-methods-and-generic-self

您的clone_with_class 方法也有一个错误,这可能会造成混淆——您忘记包含self 参数。

【讨论】:

我修复了clone_with_class 错误,谢谢。这里的问题是错误的,而不是我的测试用例。 这给出了 PyCharm 中的错误:klass is not callableclone_with_class 的第一行。我倾向于认为 PyCharm 是错误的。 根据您引用的文档,这仍然是实验性的。所以也许不是 PyCharm 被破坏的情况,但这还没有实现。 mypy 在严格模式下,我在clone_self 函数中收到警告:Returning Any from function with declared return type "Entity`-1"。此外,严格模式下的 mypy 不喜欢将 Parent 作为参数传递给 clone_with_class @PenguinBrian -- 尝试使用type(self)() 而不是self.__class__() -- 目前,type 的签名比obj.__class__ 更精确。我无法重现第二个问题(但公平地说,我使用的是从 github repo 克隆的最新版本的 mypy,所以这可能是最近修复的错误)。

以上是关于python 3.5+中的类型提示克隆函数的主要内容,如果未能解决你的问题,请参考以下文章

python 3.5的Python OpenCV导入错误

Python 3.5类型提示动态生成的实例属性

函数补充之类型提示

Python之克隆

导入模块的 Python 类型提示

Python 3.5+中的递归类型[重复]