假设我有以下脚本test.py
:
import my_library
bar = 12
def foo():
nested_bar = 21
my_library.do_things()
def nested_foo():
nested_bar += 11
not_a_variable += 1
{$ invalid_syntax
bar = 13
foo()
bar = 14
我很好奇当我跑python test.py
时到底发生了什么。显然,Python不只是逐行读取程序 - 否则在实际执行程序之前不会捕获语法错误。但这使得翻译的工作看起来有些模糊。我想知道是否有人会帮我清理一下。特别是,我想知道:
Python在什么时候意识到第13行存在语法错误?
Python在什么时候读取嵌套函数并将它们添加到foo
的范围内?
同样,Python在遇到命名空间时如何将函数foo
添加到其命名空间而不执行它?
假设my_library
导入无效。在执行任何其他命令之前,Python是否必须引发ImportError
?
假设my_library
是有效模块,但它没有函数do_things
。在执行foo()
或之前?
如果有人能指出我关于Python如何解析和执行脚本的文档,我将非常感激。
答案 0 :(得分:4)
有关modules教程的部分中的一些信息,但我认为文档没有完整的参考资料。所以,这里发生了什么。
当您第一次运行脚本或导入模块时,Python会将语法解析为AST,然后将其编译为字节码。它还没有执行任何事情;它只是将您的代码编译成一个基于堆栈的小机器的指令。这是捕获语法错误的地方。 (你可以在ast
模块,token
模块,compile
内置,grammar reference中看到所有这些内容,并在其他各个地方撒上。)< / p>
实际上,您可以独立于运行生成的代码来编译模块;这就是内置compileall
方法所做的事情。
这是第一阶段:编译。 Python只有一个其他阶段,实际上是运行代码。除了def
或lambda
中包含的语句之外,模块中的每个语句都按顺序执行。这意味着import
会在运行时发生,无论您将它们放在模块中的哪个位置。这是将它们全部放在首位的良好卫生的部分原因。 def
和class
也是如此:这些只是创建特定类型对象的语句,并且会像遇到其他任何内容一样执行。
这里唯一棘手的问题是阶段可能不止一次发生 - 例如,import
仅在运行时执行,但如果您之前从未导入过该模块,那么它必须编译,现在你回到编译时。但是&#34;外面&#34;导入它仍然是运行时,这就是为什么你可以捕获SyntaxError
抛出的import
。
无论如何,要回答您的具体问题:
在编译时。当您将其作为脚本运行时,或者将其作为模块导入时,或者使用compileall
进行编译时,或者要求Python对其进行任何理解。实际上,这可以随时发生:如果您尝试在函数中导入此模块,那么在调用该函数时,您只能获得SyntaxError
,这可能是您的程序的一半。
在执行foo
期间,因为def
和class
只是创建一个新对象并将其指定给一个名称。但Python仍然知道如何来创建嵌套函数,因为它已经编译了其中的所有代码。
将foo = lambda: 1 + 2
添加到命名空间而不执行它的方式相同。函数只是一个包含&#34;代码的对象&#34;属性 - 字面上只是一个Python字节码块。您可以将code
类型作为数据进行操作,因为是数据,与执行数据无关。尝试查看函数.__code__
,阅读&#34;代码对象&#34; the data model的一部分,甚至可以使用disassembler。 (您甚至可以使用exec
直接使用自定义本地和全局变量执行代码对象,或者更改函数使用的代码对象!)
是的,因为import
是一个普通的旧语句,就像任何其他语句一样,按顺序执行。但如果 import
之前还有其他代码,则会先运行。如果它在一个函数中,那么在该函数运行之前你不会得到错误。请注意import
,就像def
和class
一样,只是一种奇特的作业形式。
仅在执行foo()
期间。 Python无法知道其他代码是否会在该点之前向模块添加do_things
,甚至无法将my_library
完全更改为其他对象。当你提出要求时,属性查找总是及时完成。
答案 1 :(得分:1)
作为一般规则,python首先解析文件,将抽象语法树编译为字节代码,然后尝试按顺序执行它。这意味着所有语句都是逐行执行的。因此,这意味着:
nested_foo
之前调用def nested_foo()
,您会发现它会失败,因为此时尚未定义nested_foo
。ImportError
。do_things
(即您没有from my_library import do_things
),因此只有在您尝试拨打foo()
时才会出现错误。< / LI>
醇>