我在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)表示无法解析参考位置(在__add__方法中)。我应该如何指定返回类型为Position的类型?

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



但如果我错了,请纠正我,并需要使用其他语法。

最佳答案

TL; DR:如果您使用的是Python 4.0,它就可以工作。从今天(2019年)开始,在3.7+中,您必须使用将来的语句(from __future__ import annotations)启用此功能-对于Python 3.6或更低版本,请使用字符串。

我猜你有这个例外:

NameError: name 'Position' is not defined


这是因为,除非使用Python 4,否则必须先定义Position,然后才能在批注中使用它。

Python 3.7+:from __future__ import annotations

Python 3.7引入了PEP 563: postponed evaluation of annotations。使用future语句from __future__ import annotations的模块将自动将注释存储为字符串:

from __future__ import annotations

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


按计划,它将成为Python 4.0中的默认设置。由于Python仍然是一种动态类型化的语言,因此在运行时不会进行类型检查,因此输入注释应该不会对性能产生影响,对吗?错误!在python 3.7之前,键入模块以前是one of the slowest python modules in core,因此,如果您使用import typing,则在升级到3.7时会看到up to 7 times increase in performance

Python
According to PEP 484,您应该使用字符串而不是类本身:

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


如果您使用Django框架,可能会很熟悉,因为Django模型还将字符串用于正向引用(外键为self或尚未声明的外键定义)。这应该与Pycharm和其他工具一起使用。

资料来源

PEP 484PEP 563的相关部分,为您节省了旅途:


  转发参考
  
  当类型提示包含尚未定义的名称时,该定义可以表示为字符串文字,以便稍后解析。
  
  通常会发生这种情况的情况是容器类的定义,其中所定义的类出现在某些方法的签名中。例如,以下代码(简单的二进制树实现的开始)不起作用:


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



  字符串文字应包含一个有效的Python表达式(即compile(lit,``,'eval')应该是一个有效的代码对象),并且在模块完全加载后对其求值应无错误。在其中评估本地和全局名称空间的名称空间应与在其中评估同一函数的默认参数的名称空间相同。


和PEP 563:


  在Python 4.0中,将不再在定义时评估函数和变量注释。而是将字符串形式保留在相应的__annotations__词典中。静态类型检查器在行为上不会有任何区别,而在运行时使用注释的工具将必须执行推迟的评估。
  
  ...
  
  可以使用以下特殊导入从Python 3.7开始启用上述功能:


from __future__ import annotations


您可能会想做的事情

A.定义一个虚拟Position

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

class Position(object):
    pass


class Position(object):
    ...


这将摆脱NameError甚至看起来还可以:

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


B. Monkey-patch为了添加注释:

您可能想尝试一些Python元编程魔术并编写装饰器
猴子修补类定义以添加注释:

class Position:
    ...
    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


至少看起来是正确的:

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


可能麻烦太多了。

结论

如果使用3.6或更低版本,请使用包含类名称的字符串文字,在3.7中使用from __future__ import annotations即可。

关于python - 如何指定方法的返回类型与类本身相同?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58148821/

10-11 23:09
查看更多