如何键入具有封闭类类型的提示方法?

新手上路,请多包涵

我在 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) 说引用 Position 无法解析(在 __add__ 方法中)。我应该如何指定我希望返回类型为 Position 类型?

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

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

原文由 Michael van Gerwen 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 316
2 个回答

TL;DR :截至今天(2019 年),在 Python 3.7+ 中,您可以使用“未来”语句 from __future__ import annotations 打开此功能。

(由 from __future__ import annotations 启用的行为 可能 成为未来 Python 版本的默认行为,并且 成为 Python 3.10 中的默认行为。但是,3.10 中的更改在最后一刻 被恢复,现在可能根本不会发生。)

在 Python 3.6 或更低版本中,您应该使用字符串。


我猜你有这个例外:

 NameError: name 'Position' is not defined

这是因为 Position 必须先定义,然后才能在注释中使用它,除非您使用启用了 PEP 563 更改的 Python。

Python 3.7+: from __future__ import annotations

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

 from __future__ import annotations

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

这已计划成为 Python 3.10 中的默认设置,但现在已推迟此更改。由于 Python 仍然是一种动态类型语言,因此在运行时不进行类型检查,类型注释应该不会对性能产生影响,对吧?错误的!在 Python 3.7 之前,typing 模块曾经是 核心中最慢的 python 模块之一, 因此 对于涉及导入 typing 模块的代码,当你升级到 3.7 时,你会看到 性能提升高达 7 倍.

Python <3.7: 使用字符串

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

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

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

来源

PEP 484 和 PEP 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:

> # [执行](https://www.python.org/dev/peps/pep-0563/\#implementation)
>
> 在 Python 3.10 中,函数和变量注释将不再在定义时计算。相反,字符串形式将保留在相应的 `__annotations__` 字典中。静态类型检查器将看不到行为上的差异,而在运行时使用注释的工具将不得不执行延迟评估。
>
> ...
>
> ## [在 Python 3.7 中启用未来行为](https://www.python.org/dev/peps/pep-0563/\#enabling-the-future-behavior-in-python-3-7)
>
> 可以使用以下特殊导入从 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

”`

恐怕太麻烦了。

原文由 Paulo Scardine 发布,翻译遵循 CC BY-SA 4.0 许可协议

从 Python 3.11(将于 2022 年底发布)开始,您将能够使用 Self 作为返回类型。

 from typing import Self

class Position:

    def __init__(self, x: int, y: int):
        self.x = x
        self.y = y

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

Self 也包含在 typing-extensions 包中(在 PyPi 上可用),虽然不是标准库的一部分,但它是 typing 的“预览”版本 --- 模块。来自 https://pypi.org/project/typing-extensions/

typing_extensions 模块有两个相关的目的:

  • 在旧的 Python 版本上启用新的类型系统功能。例如,typing.TypeGuard 是 Python 3.10 中的新功能,但 typing_extensions 允许 Python 3.6 到 3.9 的用户也可以使用它。
  • 在新类型系统 PEP 被接受并添加到打字模块之前启用实验。

目前, typing-extensions 正式支持Python 3.7及以上版本。

原文由 chepner 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题