繁体   English   中英

类型提示:为什么 python 自引用需要引号?

[英]Typehinting: Why does python self-referencing require quotes?

根据PEP 484

当类型提示包含尚未定义的名称时,该定义可以表示为字符串文字,以便稍后解析。

我们看到了以下(非法)代码:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

这似乎是类型提示的一种直观实现,并且人们会期望由于定义了符号Tree ,所以 python 会知道它。

我的工作理论是,虽然 python 是逐行运行的,但符号Tree尚未添加到命名空间字典中,因为Tree class 仍在定义过程中。 因此,尽管符号存在,但object尚不存在。 这是正确的吗? 有没有比我给出的更细微的差别?

因此, Python 是“逐行”运行的,但那是字节码 让我们考虑以下示例:

In [3]: class Foo:
   ...:     def bar(self) -> Foo:
   ...:         return Foo()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [3], in <module>
----> 1 class Foo:
      2     def bar(self) -> Foo:
      3         return Foo()

Input In [3], in Foo()
      1 class Foo:
----> 2     def bar(self) -> Foo:
      3         return Foo()

NameError: name 'Foo' is not defined

让我们看看字节码反汇编:

In [5]: dis.dis(
   ...:     """
   ...: class Foo:
   ...:     def bar(self) -> Foo:
   ...:         return Foo()
   ...: """
   ...: )
  2           0 LOAD_BUILD_CLASS
              2 LOAD_CONST               0 (<code object Foo at 0x10b26bc90, file "<dis>", line 2>)
              4 LOAD_CONST               1 ('Foo')
              6 MAKE_FUNCTION            0
              8 LOAD_CONST               1 ('Foo')
             10 CALL_FUNCTION            2
             12 STORE_NAME               0 (Foo)
             14 LOAD_CONST               2 (None)
             16 RETURN_VALUE

Disassembly of <code object Foo at 0x10b26bc90, file "<dis>", line 2>:
  2           0 LOAD_NAME                0 (__name__)
              2 STORE_NAME               1 (__module__)
              4 LOAD_CONST               0 ('Foo')
              6 STORE_NAME               2 (__qualname__)

  3           8 LOAD_NAME                3 (Foo)
             10 LOAD_CONST               1 (('return',))
             12 BUILD_CONST_KEY_MAP      1
             14 LOAD_CONST               2 (<code object bar at 0x10b264d40, file "<dis>", line 3>)
             16 LOAD_CONST               3 ('Foo.bar')
             18 MAKE_FUNCTION            4 (annotations)
             20 STORE_NAME               4 (bar)
             22 LOAD_CONST               4 (None)
             24 RETURN_VALUE

Disassembly of <code object bar at 0x10b264d40, file "<dis>", line 3>:
  4           0 LOAD_GLOBAL              0 (Foo)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE

查看顶层,即以LOAD_BUILD_CLASS开头的层。 这实质上将__build_class__放在堆栈的顶部,这是用于实际创建 class 个对象的助手 function。 如果你想了解 function 中发生的细节,请从这里开始阅读 但出于我们的目的,它基本上选择了正确的元类(除非您另有说明,否则请type ,或者从 class 继承),并准备 class 命名空间,并执行class_object = metaclass(name, bases, namespace, **kwds) . 出于好奇, class 命名空间基本上是通过初始化它创建的, namespace = {}然后exec(body, globals(), namespace) (虽然不完全是,再次阅读更多数据 model 文档以了解细节) .

但基本上,如您所见,直到调用 class 创建 function之后,才创建变量:

    12 STORE_NAME               0 (Foo) 

现在:

    3           8 LOAD_NAME                3 (Foo)

用于创建返回注释的将在全局命名空间中搜索Foo ,但还没有找到,它会引发错误!

请注意,您可以使用from __future__ import annotations ,这将起作用,本质上,注释的评估被推迟,注释本质上作为字符串存储在注释中。 请注意,当我在使用 from __future__ import annotations后尝试它时, dis实际上似乎并没有给出不同的 output 但这可能只是 dis模块的限制

如果我们在传递给dis的代码中使用__future__导入,我们会看到:

In [6]: dis.dis(
   ...:     """
   ...: from __future__ import annotations
   ...: class Foo:
   ...:     def bar(self) -> Foo:
   ...:         return Foo()
   ...: """
   ...: )
  2           0 LOAD_CONST               0 (0)
              2 LOAD_CONST               1 (('annotations',))
              4 IMPORT_NAME              0 (__future__)
              6 IMPORT_FROM              1 (annotations)
              8 STORE_NAME               1 (annotations)
             10 POP_TOP

  3          12 LOAD_BUILD_CLASS
             14 LOAD_CONST               2 (<code object Foo at 0x10819c2f0, file "<dis>", line 3>)
             16 LOAD_CONST               3 ('Foo')
             18 MAKE_FUNCTION            0
             20 LOAD_CONST               3 ('Foo')
             22 CALL_FUNCTION            2
             24 STORE_NAME               2 (Foo)
             26 LOAD_CONST               4 (None)
             28 RETURN_VALUE

Disassembly of <code object Foo at 0x10819c2f0, file "<dis>", line 3>:
  3           0 LOAD_NAME                0 (__name__)
              2 STORE_NAME               1 (__module__)
              4 LOAD_CONST               0 ('Foo')
              6 STORE_NAME               2 (__qualname__)

  4           8 LOAD_CONST               0 ('Foo')
             10 LOAD_CONST               1 (('return',))
             12 BUILD_CONST_KEY_MAP      1
             14 LOAD_CONST               2 (<code object bar at 0x10819c240, file "<dis>", line 4>)
             16 LOAD_CONST               3 ('Foo.bar')
             18 MAKE_FUNCTION            4 (annotations)
             20 STORE_NAME               3 (bar)
             22 LOAD_CONST               4 (None)
             24 RETURN_VALUE

Disassembly of <code object bar at 0x10819c240, file "<dis>", line 4>:
  5           0 LOAD_GLOBAL              0 (Foo)
              2 CALL_FUNCTION            0
              4 RETURN_VALUE

请注意,“线”变为:

      4           8 LOAD_CONST               0 ('Foo')

无论如何,在这里阅读更多相关信息

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM