[英]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.