我如何指定方法的返回types与python中的类本身相同?

我在python 3中有以下代码:

class Position: def __init__(self, x: int, y: int): self.x = x self.y = y def __add__(self, other: Position) -> Position: return Position(self.x + other.x, self.y + other.y) 

但是我的编辑(PyCharm)说参考位置不能被parsing(在_add__方法中)。 我应该如何指定我期望的返回types是Positiontypes?

编辑:我认为这实际上是一个PyCharm问题。 它实际上在其警告中使用了信息,并且代码完成

但是纠正我的错误,并且需要使用其他语法。

如果你尝试运行这个代码,你会得到:

 NameError: name 'Position' is not defined 

这是因为Position必须先在注释中使用才能被定义。 如果您匆忙的“The Blessed Way”™是第二个,我将通过针对类似问题提出的解决方法。

1.定义一个虚拟的Position

在类定义之前,放置一个伪定义:

 class Position(object): pass class Position(object): ... 

这将摆脱NameError ,甚至可能看起来OK:

 >>> Position.__add__.__annotations__ {'other': __main__.Position, 'return': __main__.Position} 

但是呢?

 >>> for k, v in Position.__add__.__annotations__.items(): ... print(k, 'is Position:', v is Position) return is Position: False other is Position: False 

2.使用一个string

只需使用一个string,而不是类本身:

 ... def __add__(self, other: 'Position') -> 'Position': ... 

如果您使用Django框架,这可能看起来很熟悉,因为Django模型使用string作为前向引用(外键模式为self或尚未声明的外键定义)。

看起来这是根据文档推荐的方法:

转发引用

当一个types提示包含尚未定义的名称时,该定义可能被表示为一个string文字,稍后parsing。

发生这种情况通常是容器类的定义,其中定义的类出现在某些方法的签名中。 例如,以下代码(简单二叉树实现的开始)不起作用:

 class Tree: def __init__(self, left: Tree, right: Tree): self.left = left self.right = right 

为了解决这个问题,我们写:

 class Tree: def __init__(self, left: 'Tree', right: 'Tree'): self.left = left self.right = right 

string文字应该包含一个有效的Pythonexpression式(也就是说,compile(lit,'','eval')应该是一个有效的代码对象),一旦模块完全加载,它应该没有错误地评估。 在其中进行评估的本地和全局名称空间应该是相同的名称空间,其中将评估对同一个函数的默认参数。

3.猴子补丁为了添加注释:

如果您使用强制执行合同的装饰器,则可以使用此方法:

 ... def __add__(self, other): return self.__class__(self.x + other.x, self.y + other.y) Position.__add__.__annotations__['return'] = Position Position.__add__.__annotations__['other'] = Position 

至less看起来是对的:

 >>> for k, v in Position.__add__.__annotations__.items(): ... print(k, 'is Position:', v is Position) return is Position: True other is Position: True 

但是考虑一下,如果你足够聪明,可以编写一个合同执行装饰器,那么如果它是buitins.strtypes而不是buitins.strtypes的话,你可能会教它安全地eval这个注释。

结论

祝福的方式是#2。

在parsing类体时,名字“位置”是不可取的。 我不知道你是如何使用types声明,而是Python的PEP 484 – 这是大多数模式应该使用的,如果使用这些types提示说你可以简单地把这个名字作为一个string在这个点上:

 def __add__(self, other: 'Position') -> 'Position': return Position(self.x + other.x, self.y + other.y) 

检查https://www.python.org/dev/peps/pep-0484/#forward-references – 符合的工具将知道从那里解开类名称并使用它(这总是很重要的记住,Python语言本身没有任何这些注释 – 它们通常用于静态代码分析,或者在运行时可以有用于types检查的库/框架 – 但是您必须明确地设置它们)