【问题标题】:How to get Python variable annotations?如何获取 Python 变量注释?
【发布时间】:2019-07-10 09:53:11
【问题描述】:

在定义带注释字段的类/模块时,如何像在函数中一样获得注释?

class Test:
  def __init__(self):
    self.x : int
t = Test()

现在我需要来自getattr(t,'x')的'int'

【问题讨论】:

  • 我假设您知道type()。也许你可以解释为什么那不适合你。
  • x 是一个实例属性。它不存在于 Test 类中,但存在于它的实例中。

标签: python type-annotation


【解决方案1】:

使用基线 Python,如果不更改 Test 的定义,则无法执行您想要的操作。最简单的更改是在类级别注释属性:

class Test:
    x: int
    def __init__(self):
        # define self.x or not, but it needn't be annotated again

这实际上非常好; 默认情况下,假定类范围内的注释引用 instance 属性,而不是 class 属性(在类范围内赋值会创建一个类属性,但注释它没有);你必须显式地use typing.ClassVar 来表明带注释的类型只是一个类属性。 PEP 526's section on class and instance variable annotations 定义了这些行为;它们是您可以依赖的东西,而不仅仅是实施的意外。

完成此操作后,typing.get_type_hints 将在您的示例中为 Testt 返回 {'x': int}

虽然这本身就足够了,但我会注意到,在当今许多此类情况下,只要您仍然要注释,您就可以使用 the dataclasses module 简化您的代码,获取为您定义的注释和基本功能最少的打字。您的案例的简单替换代码是:

import dataclasses

@dataclasses.dataclass
class Test:
    x: int

虽然您的案例没有展示完整的功能集(它基本上只是将 __init__ 替换为装饰器),但它的作用仍然超出了人们的想象。除了为您定义__init__(它希望收到一个注释为intx 参数)以及合适的__repr____eq__,您可以轻松定义默认值(只需在注释点指定默认值,或者对于更复杂或可变的情况,请指定 dataclasses.field),您可以将参数传递给 dataclass 以使其生成可排序或不可变的实例。

就您而言,主要优势是消除冗余; x 被注释和引用一次,而不是在类级别被注释一次,然后在初始化期间使用(并且可以选择再次注释)。

【讨论】:

  • 感谢您的详细解答!我正在使用数据类,我正在开发一个项目,很快就会在 github 上开源。该项目严重依赖 Python 类型提示。我目前支持数据类,但我也想添加 Python 新样式类。
【解决方案2】:

我不确定您是否可以轻松获得self.x 的注释。

假设你的代码:

class Test:
    def __init__(self):
        self.x: int = None

t = Test()

我尝试在Testt(我希望它在的地方)中寻找__annotations__,但运气不佳。

但是,您可以做的是这种解决方法:

class Test:
    x: int
    def __init__(self):
        # annotation from here seems to be unreachable from `__annotations__`
        self.x: str

t = Test()

print(Test.__annotations__)
# {'x': <class 'int'>}
print(t.__annotations__)
# {'x': <class 'int'>}

编辑

如果您希望能够检查mypyself.x 的类型,请检查answer from @ruohola


编辑 2

请注意,mypy(至少为 v.0.560)确实会因在 class__init__ 中注释 x 而感到困惑,即它看起来像self.x 被大胆忽略:

import sys

class Test:
    x: str = "0"
    def __init__(self):
        self.x: int = 1

t = Test()

print(Test.x, t.x)
# 0 1
print(Test.x is t.x)
# False

if "mypy" in sys.modules:
    reveal_type(t.x)
    # from mypyp: annotated_self.py:14: error: Revealed type is 'builtins.str'
    reveal_type(Test.x)
    # from mypy: annotated_self.py:15: error: Revealed type is 'builtins.str'

Test.x = 2
# from mypy: annotated_self.py:17: error: Incompatible types in assignment (expression has type "int", variable has type "str")

t.x = "3"
# no complaining from `mypy`
t.x = 4
# from mypy: annotated_self.py:19: error: Incompatible types in assignment (expression has type "int", variable has type "str")

print(Test.x, t.x)
# 2 4

【讨论】:

  • 谢谢!但这不是我需要的!我只想从类构造函数中定义的字段中获取注释!
  • @Kamyar self. 变量可能是矩阵中的一个小故障(又名 bug):语法允许它们,但 mypy 没有检测到它们(至少在__init__ 内,但在使用t.x 时会被检测到),很可能是因为无论出于何种原因,它们不会在任何地方触发相应的__annotations__。也许你应该在 Python/mypy 中打开一个问题。
  • 没有冲突; x: int 是一个(未定义的)类属性,而self.x 创建一个同名的实例属性。
  • @chepner 完全正确,但mypy 被多个注释弄糊涂了,并选择忽略self.x 一个。
  • @chepner:实际上,根据PEP 526,类范围内的x: int 是(未定义的)实例属性的注释;即使x: int = 0 在类范围内也意味着具有默认值的实例属性(虽然它创建了一个类属性,但其意图是像实例变量一样使用它)。只有类属性注释using typing.ClassVar 表达类属性意图。详情请见my answer
【解决方案3】:

如果您使用mypy,您可以使用reveal_type() 来检查任何表达式的类型注释。请注意,此函数仅在运行mypy 时可用,而在正常运行时不可用。 我还使用typing.TYPE_CHECKING,在正常运行文件时不会出错,因为这个特殊常量仅被第三方类型检查器假定为True

test.py:

from typing import Dict, Optional, TYPE_CHECKING


class Test:
    def __init__(self) -> None:
        self.x: Optional[Dict[str, int]]


test = Test()
if TYPE_CHECKING:
    reveal_type(test.x)
else:
    print("not running with mypy")

在其上运行mypy 时的示例:

$ mypy test.py
test.py:10: error: Revealed type is 'Union[builtins.dict[builtins.str, builtins.int], None]'

而正常运行时:

$ python3 test.py
not running with mypy

【讨论】:

  • 在从常规解释器运行脚本时有没有办法让它工作?
  • @norok2 不幸的是没有。来自mypy的文档:reveal_typereveal_locals只有mypy能理解,Python中不存在。如果您尝试运行您的程序,则必须先删除任何 reveal_type reveal_locals 调用,然后才能运行您的代码。两者始终可用,您无需导入它们。 我使用了sys.modules 检查,以便我可以始终拥有相同的代码,并且在正常运行文件时不需要删除任何内容。
  • 是的,我看到了,我认为可以通过其他方式解决这个问题。
猜你喜欢
  • 2011-05-26
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 2022-01-14
  • 2021-11-21
  • 2020-06-12
  • 2012-07-03
相关资源
最近更新 更多