当前位置: 首页 > 知识库问答 >
问题:

如何使用封闭类的类型键入提示方法?

岳炎彬
2023-03-14

我在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__方法中)。我应该如何指定我期望的返回类型是类型位置

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

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

共有3个答案

濮阳默
2023-03-14

在解析类主体本身时,名称“Position”不可用。我不知道您是如何使用类型声明的,但是Python的PEP 484——如果使用这些类型提示,大多数模式都应该使用PEP 484,它表示您可以在此时将名称作为字符串:

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

检查关于转发引用的PEP 484部分——符合该部分的工具将知道从那里打开类名并加以利用。(记住Python语言本身对这些注释不做任何事情总是很重要的。它们通常用于静态代码分析,或者在运行时可以有一个用于类型检查的库/框架,但是你必须显式地设置它。)

更新:另外,从Python 3.7开始,请查看PEP 563。从Python3.8开始,可以从未来导入注释编写,以推迟注释的评估。前向引用类应该直接工作。

柴翰藻
2023-03-14

将类型指定为字符串是可以的,但总是让我有点恼火,因为我们基本上绕过了解析器。所以你最好不要拼错这些字串中的任何一个:

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

一个微小的变化是使用绑定的typevar,至少在声明typevar时只需编写一次字符串:

from typing import TypeVar

T = 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)
朱运诚
2023-03-14

TL;DR:从今天(2019年)开始,在Python 3.7中,您必须使用来自__future__导入注释的“未来”语句打开此功能。

(从__future__导入注释中启用的行为可能会成为Python未来版本的默认值,并将在Python 3.10中成为默认值。然而,3.10中的更改在最后一刻被恢复,现在可能根本不会发生。)

在Python3.6或更低版本中,应该使用字符串。

我猜你得到了这个例外:

NameError: name 'Position' is not defined

这是因为在注释中使用位置之前必须定义它,除非您使用启用PEP 563更改的Python。

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

from __future__ import annotations

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

这原本计划成为Python3.10中的默认设置,但是现在这个更改被推迟了。因为Python仍然是一种动态类型化语言,所以在运行时不会进行类型检查,所以类型注释应该不会对性能产生影响,对吗?错误的在Python3.7之前,typing模块曾经是核心中速度最慢的Python模块之一,因此对于涉及导入typing模块的代码,当升级到3.7时,您将看到性能提高多达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”)应该是一个有效的代码对象,并且在模块完全加载后,它应该无错误地进行计算。在其中求值的本地和全局名称空间应该是相同的名称空间,相同函数的默认参数将在其中求值。

和政治公众人物563:

在Python3.10中,函数变量注释将不再在定义时进行计算。相反,字符串形式将保留在相应的

...

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

from __future__ import annotations

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

class Position(object):
    pass


class Position(object):
    ...

这将消除名称错误,甚至看起来还可以:

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

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

可能太麻烦了。

 类似资料:
  • 问题内容: 假设我有一个函数: 如何为可以指定的东西指定返回类型? 问题答案: 您正在寻找。 由于您的返回类型可以是(从返回),也应该使用: 在有关打字的文档中,是以下内容的简写: 等价于。 其中表示类型或的值。 如果由于担心别人可能偶然发现而没有意识到它的含义而希望变得露骨,则可以始终使用: 但是我怀疑这是一个好主意,是一个指示性名称,它确实节省了几次击键。 正如@ Michael0x2a的注释

  • 我有一个python函数,可以返回或。有没有一种方法可以使用类型提示指定返回类型? 比如,这是正确的做法吗?

  • 问题内容: 我想知道用这样的签名调用静态方法的正确方法是什么: 由于某种原因,我很想这样称呼它: 但是除非我将其更改为:否则它不会编译: 我只是想知道为什么它不需要右侧的提示。而是给了我编译错误。它说它期望在右侧的提示后加上分号。第二个方法是调用该方法的正确方法吗?有人可以给我一些启示吗? 问题答案: 如图所示这里,要调用的方法的方法是: 该方法所在的类的名称在哪里。

  • 我试图在Python3.5中使用类型提示。1,代码如下: 我猜在解析类型A时,类型A还没有完全构造,这是范围界定的问题,但我不理解为什么这样的语法不正确。是否有其他方式来表达它,或者它只是一个非法的构造? 我想在基类中使用这个语法提示,它可以在树状层次结构中组成派生类。

  • 问题内容: 我正在扩展ArrayList来创建一个自定义ArrayList,可以在迭代它的同时使用常规ArrayList方法进行修改。为此,我还要创建一个迭代器。 在我的hasNext()和next()方法期间,我需要确保列表没有被修改(可以在任何其他时间修改)。因此,我需要在我的shared()块中引用我的封闭类型。 问题答案: 。因此,在您的情况下,它将为。

  • 问题内容: 我注意到python 3.5和python 3.6添加了许多有关静态类型检查的功能,因此我尝试使用以下代码(在python 3.6中为稳定版本)。 令我惊讶的是,尽管python附加到仅包含字符串的,但它却没有给我错误或警告。检测到类型错误并向我发出警告,但是它不明显,并且未在输出控制台中显示,我担心有时可能会错过它。我想要以下效果: 如果很明显如上所述我使用了错误的类型,请抛出警告或