gpt4 book ai didi

python - Python 是否评估前向引用的类型提示?

转载 作者:行者123 更新时间:2023-12-03 14:34:59 24 4
gpt4 key购买 nike

我正在查看 Forward References 上的 PEP 484 部分并注意到声明:

...that definition may be expressed as a string literal, to be resolved later.



这让我想知道,“稍后”是什么时候,什么时候?解释器稍后不会尝试将其解析为文字,那又如何呢?是否只是编写了第三方工具来执行此操作?

演示解释器结果的小例子:
class A:
def test(self, a: 'A') -> None:
pass
class B:
def test(self, a: A) -> None:
pass

>>> A().test.__annotations__
{'a': 'A', 'return': None}
>>> B().test.__annotations__
{'a': <class '__main__.A'>, 'return': None}

如果我对函数注释和类型提示的理解是正确的,那么 Python 在运行时并没有真正对它们做任何事情来提高性能,而是内省(introspection)使用允许 严格第三方应用程序,例如 linter、IDE 和静态分析工具(例如 mypy),以利用它们的可用性。那么这些工具是否会尝试解析 'A' 的类型提示?而不是将这项工作交给口译员,如果是这样,他们如何做到这一点?

更新:

通过使用 typing模块,用户代码可以执行以下操作:
>>> typing.get_type_hints(A().test)
{'a': <class '__main__.A'>, 'return': <class 'NoneType'>}
>>> typing.get_type_hints(B().test)
{'a': <class '__main__.A'>, 'return': <class 'NoneType'>}

但是,我的问题是 Python 是否有责任更新 __annotations__来自字符串文字的函数,也就是说在运行时更改:
>>> A().test.__annotations__
{'a': 'A', 'return': None}

到...
>>> A().test.__annotations__
{'a': <class '__main__.A'>, 'return': None}

如果 Python 不这样做,那么我为什么要使用字符串文字作为类型提示而不是自记录代码?第一种形式对我、用户或第三方工具有什么值(value)?

最佳答案

考虑以下代码:

class Foo:
def bar(self) -> Foo:
return Foo()

如果你尝试用 Python 运行这个程序,它实际上会在运行时崩溃:当解释器看到 bar 的定义时, Foo 的定义还没有完成。所以,由于 Foo尚未添加到全局命名空间中,我们还不能将其用作类型提示。

同样,考虑这个程序:
class Foo:
def bar(self) -> Bar:
return Bar()

class Bar:
def foo(self) -> Foo:
return Foo()

这个相互依赖的定义存在同样的问题:当我们评估 Foo 时, Bar尚未评估,因此解释器抛出异常。

这个问题有三种解决方案。首先是制作一些类型提示字符串,有效地“向前声明”它们:
class Foo:
def bar(self) -> "Foo":
return Foo()

这满足了 Python 解释器,并且不会破坏像 mypy 这样的第三方工具:他们可以在解析类型之前删除引号。主要缺点是这种语法看起来有点丑陋和笨拙。

第二种解决方案是使用类型注释语法:
class Foo:
def bar(self):
# type: () -> Foo
return Foo()

这与第一个解决方案具有相同的优点和缺点:它可以满足解释器和工具的要求,但看起来很笨拙和丑陋。它还有一个额外的好处是可以让你的代码向后兼容 Python 2.7。

第三种解决方案仅适用于 Python 3.7+ -- 使用 from __future__ import annotations指示:
from __future__ import annotations 

class Foo:
def bar(self) -> Foo:
return Foo()

这将自动使所有注释都表示为字符串。所以我们得到了第一个解决方案的好处,但没有丑陋。

这种行为最终将成为 Python future 版本的默认行为。

事实证明,自动制作所有注释字符串可以带来一些性能改进。构造类型如 List[Dict[str, int]]可能会非常昂贵:它们只是运行时的正则表达式,并且被评估为好像它们被写为 List.__getitem__(Dict.__getitem__((str, int)) .

评估这个表达式有点昂贵:我们最终执行了两个方法调用,构造一个元组,构造两个对象。这还不包括在 __getitem__ 中发生的任何额外工作。当然,方法本身 - 并且在这些方法中发生的工作最终因必要而变得不平凡。

(简而言之,他们需要构造特殊对象,以确保 List[int] 之类的类型在运行时不能以不适当的方式使用——例如在 isinstance 检查等中。)

关于python - Python 是否评估前向引用的类型提示?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55320236/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com