如何键入提示具有封闭类类型的方法? [英] How do I type hint a method with the type of the enclosing class?

查看:23
本文介绍了如何键入提示具有封闭类类型的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Python 3 中有以下代码:

类位置:def __init__(self, x: int, y: int):自我.x = x自我.y = ydef __add__(self, other: Position) ->位置:返回位置(self.x + other.x,self.y + other.y)

但我的编辑器 (PyCharm) 说无法解析参考 Position (在 __add__ 方法中).我应该如何指定我希望返回类型为 Position 类型?

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

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

解决方案

TL;DR:截至今天(2019 年),在 Python 3.7+ 中,您必须使用未来"开启此功能;声明,from __future__ import annotations.

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

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


我猜你遇到了这个异常:

NameError: 名称位置"未定义

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

Python 3.7+:从 __future__ 导入注释

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

from __future__ 导入注解班级职位:def __add__(self, other: Position) ->位置:...

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

Python <3.7:使用字符串

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

类位置:...def __add__(self, other: 'Position') ->'位置':...

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

来源

PEP 484 和 PEP 563 的相关部分,省去你的旅行:

<块引用>

转发引用

当类型提示包含尚未定义的名称时,该定义可以表示为字符串文字,稍后再解析.

这种情况经常发生的情况是容器类的定义,其中被定义的类出现在某些方法的签名中.例如,下面的代码(一个简单的二叉树实现的开始)不起作用:

类树:def __init__(自我,左:树,右:树):self.left = 左self.right = 对

为了解决这个问题,我们写:

类树:def __init__(自我,左:'树',右:'树'):self.left = 左self.right = 对

字符串字面量应该包含一个有效的 Python 表达式(即 compile(lit, '', 'eval') 应该是一个有效的代码对象),并且在模块完全加载后它应该没有错误地进行评估.计算它的本地和全局命名空间应该是相同的命名空间,相同函数的默认参数将在其中计算.

和 PEP 563:

<块引用>

实施

在 Python 3.10 中,函数和变量注释将不再在定义时进行评估.相反,字符串形式将保留在相应的 __annotations__ 字典中.静态类型检查器不会发现行为差异,而在运行时使用注释的工具将不得不执行延迟评估.

...

启用Python 3.7 中的未来行为

可以使用以下特殊导入从 Python 3.7 开始启用上述功能:

from __future__ 导入注解

你可能想做的事情

A.定义一个虚拟Position

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

类位置(对象):经过类位置(对象):...

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

<预><代码>>>>位置.__add__.__注解__{'其他':__main__.Position,'返回':__main__.Position}

是吗?

<预><代码>>>>对于位置.__add__.__annotations__.items() 中的 k,v:...打印(k,'是位置:',v是位置)返回是位置:假其他是Position: False

B.Monkey-patch 以添加注释:

您可能想尝试一些 Python 元编程魔法并编写一个装饰器对类定义进行猴子补丁以添加注释:

类位置:...def __add__(self, other):返回 self.__class__(self.x + other.x, self.y + other.y)

装饰者应该负责相当于这个:

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

至少看起来是对的:

<预><代码>>>>对于位置.__add__.__annotations__.items() 中的 k,v:...打印(k,'是位置:',v是位置)返回是位置:真其他是Position: True

可能太麻烦了.

I have the following code in 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)

But my editor (PyCharm) says that the reference Position can not be resolved (in the __add__ method). How should I specify that I expect the return type to be of type Position?

Edit: I think this is actually a PyCharm issue. It actually uses the information in its warnings, and code completion.

But correct me if I'm wrong, and need to use some other syntax.

解决方案

TL;DR: As of today (2019), in Python 3.7+ you must turn this feature on using a "future" statement, from __future__ import annotations.

(The behaviour enabled by from __future__ import annotations might become the default in future versions of Python, and was going to be made the default in Python 3.10. However, the change in 3.10 was reverted at the last minute, and now may not happen at all.)

In Python 3.6 or below, you should use a string.


I guess you got this exception:

NameError: name 'Position' is not defined

This is because Position must be defined before you can use it in an annotation, unless you are using Python with PEP 563 changes enabled.

Python 3.7+: from __future__ import annotations

Python 3.7 introduces PEP 563: postponed evaluation of annotations. A module that uses the future statement from __future__ import annotations will store annotations as strings automatically:

from __future__ import annotations

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

This had been scheduled to become the default in Python 3.10, but this change has now been postponed. Since Python still is a dynamically typed language so no type-checking is done at runtime, typing annotations should have no performance impact, right? Wrong! Before Python 3.7, the typing module used to be one of the slowest python modules in core so for code that involves importing the typing module, you will see an up to 7 times increase in performance when you upgrade to 3.7.

Python <3.7: use a string

According to PEP 484, you should use a string instead of the class itself:

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

If you use the Django framework, this may be familiar, as Django models also use strings for forward references (foreign key definitions where the foreign model is self or is not declared yet). This should work with Pycharm and other tools.

Sources

The relevant parts of PEP 484 and PEP 563, to spare you the trip:

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.

and 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

Things that you may be tempted to do instead

A. Define a dummy Position

Before the class definition, place a dummy definition:

class Position(object):
    pass


class Position(object):
    ...

This will get rid of the NameError and may even look OK:

>>> Position.__add__.__annotations__
{'other': __main__.Position, 'return': __main__.Position}

But is it?

>>> 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 in order to add the annotations:

You may want to try some Python metaprogramming magic and write a decorator to monkey-patch the class definition in order to add annotations:

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

The decorator should be responsible for the equivalent of this:

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

At least it seems right:

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

Probably too much trouble.

这篇关于如何键入提示具有封闭类类型的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆