为了账号安全,请及时绑定邮箱和手机立即绑定

如何指定方法的返回类型与类本身相同?

如何指定方法的返回类型与类本身相同?

守着星空守着你 2019-12-18 18:13:57
如何指定方法的返回类型与类本身相同?我在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问题。它实际上使用警告和代码完成中的信息。但是,如果我错了,需要使用其他语法来纠正我。
查看完整描述

3 回答

?
精慕HU

TA贡献1845条经验 获得超8个赞

如果您使用的是Python4.0,那么它只是起作用了。从今天(2019年)开始,在3.7+中,您必须使用将来的语句(from __future__ import annotations)-对于Python3.6或更低的版本,请使用字符串。

我想你有个例外:

NameError: name 'Position' is not defined

这是因为Position除非使用Python 4,否则必须在注释中使用它之前定义它。

Python 3.7+:from __future__ import annotations

Python 3.7Pep 563:推迟对说明的评价..使用未来语句的模块from __future__ import annotations将自动将注释存储为字符串:

from __future__ import annotationsclass Position:
    def __add__(self, other: Position) -> Position:
        ...

这将成为Python4.0中的默认设置。因为Python仍然是一种动态类型语言,所以在运行时不进行类型检查,所以输入注释不应该对性能产生影响,对吗?不对!在python 3.7之前,类型模块以前是核心中最慢的python模块之一所以如果你import typing你会看到业绩提高7倍升级到3.7。

Python<3.7:使用字符串

根据PEP 484,您应该使用字符串而不是类本身:

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

如果您使用Django框架,这可能很熟悉,因为Django模型也使用字符串进行前向引用(外键定义,其中外接模型是self或尚未宣布)。这应该适用于PyCharm和其他工具。

来源

的相关部分佩普484佩普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表达式(即编译(LIT,‘val’)应该是一个有效的代码对象),并且一旦模块被完全加载,它应该在没有错误的情况下进行计算。计算它的本地和全局命名空间应该是相同的名称空间,其中将计算相同函数的默认参数。

和PEP 563:

在Python4.0中,函数和变量注释将不再在定义时计算。相反,字符串窗体将保留在相应的__annotations__字典。静态类型检查器在行为上没有区别,而在运行时使用注释的工具将不得不执行延迟的评估。

...

可以使用以下特殊导入从Python3.7开始启用上面描述的功能:

from __future__ import annotations

你可能会被诱惑去做的事情

A.定义假人Position

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

class Position(object):
    passclass 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: Falseother is Position: False

B.为添加注释而进行的猴子补丁程序:

您可能需要尝试一些Python元编程魔术,并编写一个修饰器来对类定义进行猴子修补,以便添加注释:

class Position:
    ...
    def __add__(self, other):
        return self.__class__(self.x + other.x, self.y + other.y)

装修人员应对此负责:

Position.__add__.__annotations__['return'] = PositionPosition.__add__.__annotations__['other'] = Position

至少这似乎是对的:

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

可能太麻烦了。

结语

如果使用3.6或更低版本,请使用包含类名的字符串文本,在3.7中使用from __future__ import annotations而且它会起作用的。



查看完整回答
反对 回复 2019-12-19
?
胡说叔叔

TA贡献1804条经验 获得超8个赞

将类型指定为String是可以的,但总是会让我感到有些烦躁,因为我们基本上是在绕过解析器。所以你最好不要拼错这些文字字符串中的任何一个:

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

一个细微的变化是使用绑定的type var,至少在声明type var时,您必须只写一次字符串:

from typing import TypeVarT = TypeVar('T', bound='Position')class Position:

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



查看完整回答
反对 回复 2019-12-19
  • 3 回答
  • 0 关注
  • 334 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信