真题讲解
❓请问 Python 程序是怎么运行的?是编译成机器码后在执行的吗?试着与 C 、C++ 、Java 、Shell 等常见语言比较说明。
不少初学者对 Python 存在误解,以为它是类似 Shell 的解释性脚本语言,其实并不是。虽然执行 Python 程序的 python 命令也被称为 Python 解释器,但它其实包含一个 编译器 和一个 虚拟机 。
当我们在命令行敲下 python xxxx.py
时,python 命令中的编译器首先登场,将 Python 代码编译成 代码 对象。代码 对象包含 字节码 以及执行字节码所需的 名字 以及 常量 。
当编译器完成编译动作后,接力棒便传给 虚拟机 。虚拟机 维护执行上下文,逐行执行 字节码 指令。执行上下文中最核心的 名字空间 ,便是由 虚拟机 维护的。
因此,Python 程序的执行原理其实更像 Java ,可以用两个词来概括—— 虚拟机 和 字节码 。不同的是,Java 编译器 javac 与 虚拟机 java 是分离的,而 Python 将两者整合成一个 python 命令。此外,Java 程序执行前必须先完整编译,而 Python 则允许程序启动后再编译并加载需要执行的模块。
❓ pyc 文件保存什么东西,有什么作用?
Python 程序执行时需要先由 编译器 编译成 代码 对象,然后再交由 虚拟机 来执行。不管程序执行多少次,只要源码没有变化,编译后得到的代码对象就肯定是一样的。因此,Python 将代码对象序列化并保存到 pyc 文件中。当程序再次执行时,Python 直接从 pyc 文件中加载代码对象,省去编译环节。当然了,当 py 源码文件改动后,pyc 文件便失效了,这时 Python 必须重新编译 py 文件。
❓如何查看 Python 程序的字节码?
Python 标准库中的 dis 模块,可以对 代码 对象以及 函数 对象进行反编译,并显示其中的 字节码 。
例如,对于函数 add ,通过 code 字段取到它的 代码 对象,并调用 dis 进行反编译:
|
|
当然了,直接将 函数 对象传给 dis 也可以:
|
|
❓ Python 中变量交换有两种不同的写法,示例如下。这两种写法有什么区别吗?那种写法更好?
|
|
这两种写法都能实现变量交换,表面上看第一种写法更加简洁明了,似乎更优。那么,在优雅的外表下是否隐藏着不为人知的性能缺陷呢?想要找打答案,唯一的途径是研究字节码:
|
|
|
|
从字节码上看,第一种写法需要的指令条目也更少:先将两个变量依次加载到栈,然后一条 ROT_TWO 指令将栈中的两个变量交换,最后再将变量依次写回去。注意到,变量加载的顺序与 = 右边一致,写回顺序与 = 左边一致。
而且,ROT_TWO 指令只是将栈顶两个元素交换位置,执行起来比 LOAD_NAME 和 STORE_NAME 都要快。
至此,我们可以得到结论了—— 第一种变量交换写法更优 :
- 代码简洁明了,不拖泥带水;
- 不需要辅助变量 tmp ,节约内存;
- ROT_TWO 指令比一个 LOAD_NAME STORE_NAME 指令对更有优势,执行效率更高;
更多面试真题
❓请解释 is 和 == 这两个操作的区别。
❓在 Python 中与 None 比较时,为什么要用 is None 而不是 == None ?
❓请问以下程序输出什么?为什么?
上述的问题,你是否都能对答如流呢?点击 阅读原文,获取详细题解!
【Python源码剖析】系列文章首发于公众号【小菜学编程】,敬请关注: