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

标签 python python-3.x pycharm type-hinting python-typing

我在 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 问题。它实际上在其警告和代码完成中使用了这些信息。

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

最佳答案

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

(from __future__ import annotations 启用的行为可能成为 future Python 版本的默认行为,was going 将成为 Python 3.10 的默认行为。但是, 3.10 中的更改 was reverted 在最后一刻发生,现在可能根本不会发生。)

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


我猜你遇到了这个异常:

NameError: name 'Position' is not defined

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

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 3.10 中的默认设置,但现在已推迟此更改。由于 Python 仍然是一种动态类型语言,因此在运行时不进行类型检查,类型注释应该不会对性能产生影响,对吧?错误的!在 Python 3.7 之前,打字模块曾经是 one of the slowest python modules in core所以对于涉及导入typing 模块的代码,您将看到一个up to 7 times increase in performance。当您升级到 3.7 时。

Python <3.7: 使用字符串

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

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

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

来源

PEP 484 和 PEP 563 的相关部分,免去您的旅途:

Forward references

When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.

A situation where this occurs commonly is the definition of a container class, where the class being defined occurs in the signature of some of the methods. For example, the following code (the start of a simple binary tree implementation) does not work:

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

To address this, we write:

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

The string literal should contain a valid Python expression (i.e., compile(lit, '', 'eval') should be a valid code object) and it should evaluate without errors once the module has been fully loaded. The local and global namespace in which it is evaluated should be the same namespaces in which default arguments to the same function would be evaluated.

和 PEP 563:

Implementation

In Python 3.10, function and variable annotations will no longer be evaluated at definition time. Instead, a string form will be preserved in the respective __annotations__ dictionary. Static type checkers will see no difference in behavior, whereas tools using annotations at runtime will have to perform postponed evaluation.

...

Enabling the future behavior in Python 3.7

The functionality described above can be enabled starting from Python 3.7 using the following special import:

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.猴子补丁为了添加注释:

您可能想尝试一些 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

可能太麻烦了。

关于python - 如何键入具有封闭类类型的提示方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45636816/

相关文章:

python-3.x - 即使它们之间的唯一区别是脚本,我是否需要多个运行配置 - 每个 Python 文件一个 - 在 Pycharm 中?

python - PyCharm 和 IronPython 代码完成?

python - 有没有办法从 django 模板生成包含非 ascii 符号的 pdf?

jquery - Django 模板使用带有 {{}} 的 jquery 脚本

Python - 我的频率函数效率低下

python - 如何自定义 python 记录器行为?

python-3.x - 如何在 MacOS 的 Sublime Text 3 中更改首选编码

python - 使用 PyCharm 导入 Tkinter

python - pip 没有 SSL 证书检查?

python - 理解位置参数的直观方法不应该跟在关键字参数之后