在python 3.5+;
假设我有以下课程:在python 3.5+;,python,python-3.x,python-3.5,type-hinting,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()
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()
但是,正如所写的,这不会通过类型检查,因为克隆被认为是类型父项
而不是子项
我尝试使用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\u和\u class
的解决方案是否正确?也许PyCharm抱怨是不对的?否则,需要做什么来修复上述代码
这应该是TypeVar('Entity',bind='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错误地抱怨,我就可以针对它提交bug
对于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"
这些错误是意料之中的。我不知道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()
请注意,我为clone\u self
指定了一个特定的类型,这让我们可以根据需要更精确地键入返回类型。您可以在此处了解有关使用通用self的更多信息:
您的
clone\u with_class
方法中也有一个bug,这可能会混淆问题——您忘记包含self
参数。我修复了clone\u with_class
bug,谢谢。这里的问题是个错误,不是我的测试用例。这给出了PyCharm中的错误:klass不可调用
,用于clone\u with\u class
中的第一行。我倾向于认为PyCharm是错误的。根据您引用的文档,这仍然是实验性的。因此,PyCharm可能不会被破坏,但这还没有实现。mypy处于严格模式时,我在clone\u self
函数中收到一条警告:从声明返回类型为“Entity`-1”的函数返回Any。此外,严格模式下的mypy不喜欢将Parent
作为参数传递给clone\u with\u class
@penguinbian--尝试使用type(self)(
而不是self.\uu class\uuuuuuuuuuuuu()
--目前,type
的签名比obj更精确。我无法重新编写第二个问题(虽然公平地说,我使用的是从github repo克隆的最新版本的mypy,所以这可能是最近修复的错误)。我添加了更多测试用例来测试所需的行为。