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 可能会成为 Python future 版本的默认行为,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+:从 __future__ 导入注释

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

可能太麻烦了。

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

相关文章:

Java Wildfly POST 方法 multipart/form-data : "Unable to get boundary..."

python - 在 PyCharm 的虚拟环境中更新 Tensorflow 二进制文件以使用 AVX2

pycharm - 如何像在命令行上一样使用PyCharm重定向输入和输出?

css - PyCharm CSS 编辑 : Disable auto-completion

python - 无效的 float 。消息错误Python

python - 在 flask 中创建 query_string 时出错

python - 为什么我的插入排序函数返回 None?

python - 如何保存分辨率大于1080p的图片?

python-3.x - 如何在Python中动态更新cv2窗口标题的FPS

python - matplotlib 图形的 latex 渲染文本的中心标题