类型提示:为什么 python 自引用需要引号?
Typehinting: Why does python self-referencing require quotes?
根据PEP 484,
When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.
我们看到了以下(非法)代码:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
这似乎是类型提示的一种直观实现,人们会期望由于定义了符号 Tree
,所以 python 会知道它。
我的工作理论是,虽然 python 是逐行 运行,但符号 Tree
尚未添加到命名空间字典中,因为 Tree
class 仍在定义中。因此,尽管 symbol 存在,但 object 尚不存在。这是正确的吗?有没有比我给出的更细微的差别?
因此,Python 是 运行 “line-by-line”,但那是 字节码 。让我们考虑以下示例:
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 对象的辅助函数。如果你想了解函数中发生的细节,start reading from here。但出于我们的目的,它基本上选择了正确的 metaclass(所以 type
除非你另有说明,或者继承自 class ),并准备 class 命名空间,并且 class_object = metaclass(name, bases, namespace, **kwds)
。出于好奇,class 命名空间本质上是通过初始化它创建的,namespace = {}
然后是 exec(body, globals(), namespace)
(虽然不完全是,再次阅读更多关于 nitty-gritty 的数据模型文档详情)。
但基本上,如您所见,直到 class 创建函数被调用后,变量才被创建:
12 STORE_NAME 0 (Foo)
现在,:
3 8 LOAD_NAME 3 (Foo)
用于创建 return 注释的将在全局命名空间中搜索 Foo
,但还没有找到,它会引发错误!
注意,您可以使用 from __future__ import annotations
,这将 工作 ,本质上,注释的评估被推迟,注释本质上作为字符串存储在注释中. 请注意,当我在使用 from __future__ import annotations
后尝试时,dis
实际上似乎并没有给出不同的输出,但这可能只是 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')
无论如何,阅读更多相关内容 here
根据PEP 484,
When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.
我们看到了以下(非法)代码:
class Tree:
def __init__(self, left: Tree, right: Tree):
self.left = left
self.right = right
这似乎是类型提示的一种直观实现,人们会期望由于定义了符号 Tree
,所以 python 会知道它。
我的工作理论是,虽然 python 是逐行 运行,但符号 Tree
尚未添加到命名空间字典中,因为 Tree
class 仍在定义中。因此,尽管 symbol 存在,但 object 尚不存在。这是正确的吗?有没有比我给出的更细微的差别?
因此,Python 是 运行 “line-by-line”,但那是 字节码 。让我们考虑以下示例:
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 对象的辅助函数。如果你想了解函数中发生的细节,start reading from here。但出于我们的目的,它基本上选择了正确的 metaclass(所以 type
除非你另有说明,或者继承自 class ),并准备 class 命名空间,并且 class_object = metaclass(name, bases, namespace, **kwds)
。出于好奇,class 命名空间本质上是通过初始化它创建的,namespace = {}
然后是 exec(body, globals(), namespace)
(虽然不完全是,再次阅读更多关于 nitty-gritty 的数据模型文档详情)。
但基本上,如您所见,直到 class 创建函数被调用后,变量才被创建:
12 STORE_NAME 0 (Foo)
现在,:
3 8 LOAD_NAME 3 (Foo)
用于创建 return 注释的将在全局命名空间中搜索 Foo
,但还没有找到,它会引发错误!
注意,您可以使用 from __future__ import annotations
,这将 工作 ,本质上,注释的评估被推迟,注释本质上作为字符串存储在注释中. 请注意,当我在使用 .from __future__ import annotations
后尝试时,dis
实际上似乎并没有给出不同的输出,但这可能只是 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')
无论如何,阅读更多相关内容 here