当前位置: 首页 > 面试题库 >

Python编译/解释过程

瞿兴朝
2023-03-14
问题内容

我试图更清楚地了解python编译器/解释器的过程。不幸的是,我没有上过口译课,也没有读过很多关于口译的文章。

基本上,我现在所了解的是,.py文件中的Python代码首先被编译为python字节码(我认为这是我偶尔看到的.pyc文件?)。接下来,字节码被编译成机器码,这是处理器真正理解的语言。差不多,我已经读过这个线程。为什么python在解释之前将源代码编译为字节码?

考虑到我对编译器/解释器的知识几乎不存在,有人可以给我一个关于整个过程的很好的解释吗?或者,如果不可能的话,也许给我一些资源,以快速概述编译器/解释器?

谢谢


问题答案:

除非您使用某些特殊的实现(例如pypy),否则字节码实际上不会解释为机器代码。

除此之外,您的描述正确。字节码被加载到Python运行时中,并由虚拟机解释,该虚拟机是一段代码,它读取字节码中的每条指令并执行所指示的任何操作。您可以在dis模块中看到此字节码,如下所示:

>>> def fib(n): return n if n < 2 else fib(n - 2) + fib(n - 1)
... 
>>> fib(10)
55
>>> import dis
>>> dis.dis(fib)
  1           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (2)
              6 COMPARE_OP               0 (<)
              9 JUMP_IF_FALSE            5 (to 17)
             12 POP_TOP             
             13 LOAD_FAST                0 (n)
             16 RETURN_VALUE        
        >>   17 POP_TOP             
             18 LOAD_GLOBAL              0 (fib)
             21 LOAD_FAST                0 (n)
             24 LOAD_CONST               1 (2)
             27 BINARY_SUBTRACT     
             28 CALL_FUNCTION            1
             31 LOAD_GLOBAL              0 (fib)
             34 LOAD_FAST                0 (n)
             37 LOAD_CONST               2 (1)
             40 BINARY_SUBTRACT     
             41 CALL_FUNCTION            1
             44 BINARY_ADD          
             45 RETURN_VALUE        
>>>

详细说明

了解上面的代码永远不会由您的CPU执行非常重要。它也永远不会转换成某种东西(至少不是在Python的官方C实现上)。CPU执行虚拟机代码,该虚拟机代码执行字节码指令指示的工作。当解释器要执行该fib功能时,它会一次读取一条指令,然后执行指令。它查看第一条指令,LOAD_FAST 0从而从保存参数的任何地方获取参数0(n传递给fib),并将其压入解释器的堆栈(Python的解释器是堆栈计算机)。在阅读下一条说明时,LOAD_CONST 1,它将从该函数拥有的常量集合中获取一个常量1(在这种情况下恰好是2),并将其压入堆栈。您实际上可以看到以下常量:

>>> fib.func_code.co_consts
(None, 2, 1)

下一条指令,COMPARE_OP 0告诉解释器弹出两个最顶部的堆栈元素,并在它们之间进行不等式比较,将布尔结果推回堆栈。第四条指令基于布尔值确定是向前跳五条指令还是继续下一条指令。所有这些动词都解释if n < 2了条件表达式中的部分fib。弄清楚其余fib字节码的含义和行为,对您而言将是非常有启发性的练习。唯一的一个,我不知道的是POP_TOP,我猜想JUMP_IF_FALSE定义为将其布尔参数保留在堆栈上而不是将其弹出,因此必须显式弹出它。

更具指导意义的是检查原始字节码,fib从而:

>>> code = fib.func_code.co_code
>>> code
'|\x00\x00d\x01\x00j\x00\x00o\x05\x00\x01|\x00\x00S\x01t\x00\x00|\x00\x00d\x01\x00\x18\x83\x01\x00t\x00\x00|\x00\x00d\x02\x00\x18\x83\x01\x00\x17S'
>>> import opcode
>>> op = code[0]
>>> op
'|'
>>> op = ord(op)
>>> op
124
>>> opcode.opname[op]
'LOAD_FAST'
>>>

因此,您可以看到字节码的第一个字节是LOAD_FAST指令。下一对字节'\x00\x00'(16位中的数字0)是的参数LOAD_FAST,并告诉字节码解释器将参数0加载到堆栈上。



 类似资料:
  • 问题内容: 我正在构建一个专用的嵌入式Python解释器,并希望避免依赖于动态库,因此我想改用静态库来编译解释器(例如,不编译)。 我还想静态链接Python标准库中所有的动态库。我知道可以使用来完成此操作,但是有没有一种替代方法可以一步完成呢? 问题答案: 我发现了这一点(主要是关于Python模块的静态编译): http://bytes.com/groups/python/23235-buil

  • 问题内容: 为什么我们说Java是一种编译和解释语言? (编译和解释)这样做有什么好处? 问题答案: Java在编译时被编译为中间的“字节码”。这与像C之类的语言在编译时被编译为机器语言相反。Java字节代码无法像编译的C代码那样直接在硬件上执行。相反,字节码必须在运行时由JVM(Java虚拟机)解释才能执行。像C这样的语言的主要缺点是,当编译该二进制文件时,该二进制文件将仅在一种特定的体系结构(

  • 那么,让我看看我是否明白了这一点。 > 当我们说编译器和解释器之间的区别时,解释器将高级指令翻译成中间形式,然后执行。[我认为编译器也将高级指令翻译成中间形式,但此刻它生成目标代码而不是执行它,对吗?] 解释器一次读取一条指令或一行的源代码,将该行转换为机器代码并执行它。[解释器本身不会将代码转换为机器代码,它会使用ist自己的预编译函数评估指令(在解析之后)。例如,高级语言中的Add表达式将使用

  • 翻译的结果是什么?它会把高级语言变成汇编语言还是机器语言,即二进制代码? 如果是后者,这是否意味着解释语言与介绍集没有关系? 一种语言只能有一个口译员?是因为解释器不做任何优化,所以我们不需要找到更好的解释器吗?

  • 问题内容: PHP是编译还是解释? 问题答案: PHP是一种解释型语言。可以解释PHP的二进制文件已编译,但是您编写的文件却被解释。

  • 问题内容: 我正在学习Java,以下内容对我来说有些混乱。我了解的是: Java编译器 →Java编译器仅将程序转换为文件,这意味着将我们的源代码转换为字节码(这是使Java平台独立的虚拟机(JVM)的操作码的列表)。 Java Interpreter →仅“解释”代码,而 没有 将其转换为本地机器代码。它将一条字节码的每条指令作为一条命令一一执行并执行,而不管同一条指令出现多少次。这就是为什么它