代码之家  ›  专栏  ›  技术社区  ›  AJMansfield

如何用尚未声明的类的类型注释函数?[副本]

  •  0
  • AJMansfield  · 技术社区  · 7 年前

    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)
    

    __add__ 方法)。我应该如何指定返回类型为 Position

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

    0 回复  |  直到 6 年前
        1
  •  552
  •   mkrieger1 djuarezg    6 年前

    :如果您使用的是Python4.0条它只是起作用。从今天(2019年)起3.7条+必须使用将来的语句打开此功能( from __future__ import annotations )-对于Python3.6条或者下面用一个字符串。

    NameError: name 'Position' is not defined
    

    这是因为 Position 必须先定义,然后才能在批注中使用它,除非您使用的是Python4。

    从未来导入批注

    PEP 563: postponed evaluation of annotations . 使用未来语句的模块 从未来导入批注

    from __future__ import annotations
    
    class Position:
        def __add__(self, other: Position) -> Position:
            ...
    

    这将成为Python中的默认设置4.0条. 因为Python仍然是一种动态类型语言,所以在运行时不会进行类型检查,所以输入注释应该不会影响性能,对吧?错了!在python之前3.7条打字模块过去是 one of the slowest python modules in core 所以 import typing up to 7 times increase in performance 当您升级到3.7条.

    巨蟒<3.7条:使用字符串

    According to PEP 484

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

    如果您使用Django框架,这可能很熟悉,因为Django模型还使用字符串作为前向引用(外键定义,其中外键模型是 self

    来源

    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
    

    以及PEP 563:

    在Python中4.0条,函数和变量注释将不再在定义时求值。相反,字符串形式将保留在 __annotations__

    ...

    from __future__ import annotations
    

    你可能想做的事

    A、 定义虚拟对象

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

    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
    

    您可能想尝试一些Python元编程魔术,并编写一个decorator 要对类定义进行猴子修补以添加批注,请执行以下操作:

    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
    

    可能太麻烦了。

    如果你正在使用3.6条或者在下面使用包含类名的字符串文字,在3.7条使用 它会起作用的。

        2
  •  15
  •   jsbueno    6 年前

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

    支票 https://www.python.org/dev/peps/pep-0484/#forward-references -符合这一要求的工具将知道如何从那里展开类名并加以利用(务必记住,Python语言本身不做任何这些注释—它们通常用于静态代码分析,或者可以有一个库/框架用于运行时的类型检查—但您必须显式地设置那)。

    更新 pep-563 -从Python开始3.8条有可能写 from __future__ import annotations 要延迟对注释的求值,向前引用类应该很简单。

        3
  •  13
  •   vbraun    8 年前

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

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

    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)
    
        4
  •  8
  •   Yvon DUTAPIS    6 年前

    __qualname__

    class MyClass:
        @classmethod
        def make_new(cls) -> __qualname__:
            return cls()
    

    通过这样做,重命名类并不意味着修改类型提示。但我个人并不期望聪明的代码编辑器能很好地处理这个表单。

    推荐文章