【问题标题】:Can I use __qualname__ in a Python type hint with postponed annotation evaluation?我可以在带有延迟注释评估的 Python 类型提示中使用 __qualname__ 吗?
【发布时间】:2019-12-17 13:44:56
【问题描述】:

我喜欢将__qualname__ 用于工厂式类方法的返回类型注释,因为它不会对类名进行硬编码,因此可以保持工作子类(参见this answer)。

class Foo:
    @classmethod
    def make(cls) -> __qualname__:
        return cls()

目前这似乎工作正常,但我不确定这是否仍然可以通过延迟评估注释 (PEP 563):PEP says

注释只能使用模块范围内的名称,因为使用本地名称的延迟评估不可靠(typing.get_type_hints() 解析的类级名称除外)。

PEP 还有says:

get_type_hints() 函数自动为函数和类解析 globalns 的正确值。它还会自动为类提供正确的localns

但是,下面的代码

from __future__ import annotations

import typing

class Foo():
    @classmethod
    def make(cls) -> __qualname__:
        return cls()

print(typing.get_type_hints(Foo.make))

失败

  File "qualname_test.py", line 11, in <module>
    print(typing.get_type_hints(Foo.make))
  File "/var/local/conda/envs/py37/lib/python3.7/typing.py", line 1004, in get_type_hints
    value = _eval_type(value, globalns, localns)
  File "/var/local/conda/envs/py37/lib/python3.7/typing.py", line 263, in _eval_type
    return t._evaluate(globalns, localns)
  File "/var/local/conda/envs/py37/lib/python3.7/typing.py", line 467, in _evaluate
    eval(self.__forward_code__, globalns, localns),
  File "<string>", line 1, in <module>
NameError: name '__qualname__' is not defined

注释 __future__ 导入会使代码再次工作,在这种情况下它会输出

{'return': <class '__main__.Foo'>}

正如预期的那样。

我的用例 __qualname__ 是否受支持(这意味着 get_type_hints 有问题),还是 PEP 563 无法使用这种方法?

【问题讨论】:

  • 您对__qualname__ 的使用不正确。考虑class Bar(Foo): passBar.make() 不返回 Foo 的实例;它返回一个Bar 的实例。
  • 正确的类型提示可能是TypeVar: def make(cls: Type[T]) -&gt; T
  • @chepner:你说得对,我没有想到这一点。但是,随着延迟评估应该开始起作用,对吗?因为那么Bar.make 的注解应该在Bar 的上下文中进行评估,其中__qualname__ 指的是Bar
  • FWIW:虽然mypy 目前不支持__qualname__,但mypy/#6473 存在问题。它已被接受为描述错误并正在处理中。我不知道get_type_hints 中断是否是一个错误——它似乎对所有类级属性(包括类型和别名)都中断了,但还没有错误报告。

标签: python language-lawyer


【解决方案1】:

Foo.make 并不总是返回 Foo 的实例(这是 __qualname__ 扩展的内容)。它返回作为参数接收的类的实例,不一定是Foo。考虑:

>>> class Foo:
...     @classmethod
...     def make(cls):
...         return cls()
...
>>> class Bar(Foo):
...     pass
...
>>> type(Bar.make())
<class '__main__.Bar'>

正确的类型提示应该是类型变量:

T = TypeVar("T", bound="Foo")

class Foo:
    @classmethod
    def make(cls: Type[T]) -> T:
        return cls()

【讨论】:

  • 你说得对,我没有想到这一点。但是,随着延迟评估应该开始起作用,对吗?因为那么Bar.make 的注解应该在Bar 的上下文中进行评估,其中__qualname__ 指的是Bar。假设__qualname__仍然可以在注解中使用,即。
  • 使用from __future__ import annotations,如果你尝试使用__qualname__作为返回类型,mypy会报告“error: Name '__qualname__' is not defined
  • __qualname__ 仅存在于class 语句的命名空间内,而不存在于全局范围内,因此(我认为)当mypy 尝试使用它时,它已超出范围.
  • 是的,__qualname__ 只存在于class 命名空间中。问题是,在推迟评估的情况下,这是否应该起作用(在这种情况下, mypy 和 get_type_hints 会出错)。
  • __qualname__ 在这里使用是完全错误的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-26
  • 2019-06-19
  • 1970-01-01
相关资源
最近更新 更多