CPython:为什么一个三行脚本需要在解释器中执行远远超过3个周期?

2024-10-06 12:09:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我刚刚看了Philip Guo的this Youtube lecture关于CPython内部的文章,我对一件事感到困惑。你知道吗

在25:55,他修改了CPython的C源代码,在运行所有字节码指令的无休止循环的开始处插入printf(“hello\n”);您可以通过以下方法执行相同的操作:

  • 下载python2.7c源代码
  • 打开文件Python/ceval.c
  • 找到无休止的求值循环的开始,for (;;) {
  • 添加行printf('hello\n');作为无尽循环的第一行。你知道吗
  • 运行configuremake构建Python二进制文件。你知道吗

他写了三行字测试.py地址:

X = 1
Y = 2
print X + Y

问题是,当他跑的时候测试.py有了经过修改的解释器,为什么在我们看到“3”之前会有这么多“你好”?你知道吗

三行代码应该编译成几个字节码指令,加载值1,加载值2和调用print的指令,所以我可以想象当它执行从测试.py,我们应该只看到几个“你好”。你知道吗

那么编译器在编译外部Python脚本之前实际上会生成许多内部字节码指令?你知道吗


Tags: 文件pyhello字节源代码youtube文章指令
1条回答
网友
1楼 · 发布于 2024-10-06 12:09:58

有两个原因让你看到这么多hello的印刷品:

  • Python并不是每个可能的Python语句都有一个特殊的字节码。相反,语句将使用字节码的组合。你知道吗
  • Python解释器导入一系列Python模块以开始运行。可以使用-v开关运行常规的Python解释器来查看每次导入的内容。每个模块都由多个语句组成,因此在进入正在运行的小脚本之前,需要经历相当多的字节码。你知道吗

如果我将这3行代码放到test.py中,并使用未修改的python2.7二进制代码运行它,使用-v开关,我会看到:

$ python2.7 -v test.py
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
# /..../lib/python2.7/site.pyc matches /..../lib/python2.7/site.py
import site # precompiled from /..../lib/python2.7/site.pyc
# /..../lib/python2.7/os.pyc matches /..../lib/python2.7/os.py
import os # precompiled from /..../lib/python2.7/os.pyc
import errno # builtin
import posix # builtin
# /..../lib/python2.7/posixpath.pyc matches /..../lib/python2.7/posixpath.py
import posixpath # precompiled from /..../lib/python2.7/posixpath.pyc
# /..../lib/python2.7/stat.pyc matches /..../lib/python2.7/stat.py
import stat # precompiled from /..../lib/python2.7/stat.pyc
# /..../lib/python2.7/genericpath.pyc matches /..../lib/python2.7/genericpath.py
import genericpath # precompiled from /..../lib/python2.7/genericpath.pyc
# /..../lib/python2.7/warnings.pyc matches /..../lib/python2.7/warnings.py
import warnings # precompiled from /..../lib/python2.7/warnings.pyc
# /..../lib/python2.7/linecache.pyc matches /..../lib/python2.7/linecache.py
import linecache # precompiled from /..../lib/python2.7/linecache.pyc
# /..../lib/python2.7/types.pyc matches /..../lib/python2.7/types.py
import types # precompiled from /..../lib/python2.7/types.pyc
# /..../lib/python2.7/UserDict.pyc matches /..../lib/python2.7/UserDict.py
import UserDict # precompiled from /..../lib/python2.7/UserDict.pyc
# /..../lib/python2.7/_abcoll.pyc matches /..../lib/python2.7/_abcoll.py
import _abcoll # precompiled from /..../lib/python2.7/_abcoll.pyc
# /..../lib/python2.7/abc.pyc matches /..../lib/python2.7/abc.py
import abc # precompiled from /..../lib/python2.7/abc.pyc
# /..../lib/python2.7/_weakrefset.pyc matches /..../lib/python2.7/_weakrefset.py
import _weakrefset # precompiled from /..../lib/python2.7/_weakrefset.pyc
import _weakref # builtin
# /..../lib/python2.7/copy_reg.pyc matches /..../lib/python2.7/copy_reg.py
import copy_reg # precompiled from /..../lib/python2.7/copy_reg.pyc
import encodings # directory /..../lib/python2.7/encodings
# /..../lib/python2.7/encodings/__init__.pyc matches /..../lib/python2.7/encodings/__init__.py
import encodings # precompiled from /..../lib/python2.7/encodings/__init__.pyc
# /..../lib/python2.7/codecs.pyc matches /..../lib/python2.7/codecs.py
import codecs # precompiled from /..../lib/python2.7/codecs.pyc
import _codecs # builtin
# /..../lib/python2.7/encodings/aliases.pyc matches /..../lib/python2.7/encodings/aliases.py
import encodings.aliases # precompiled from /..../lib/python2.7/encodings/aliases.pyc
# /..../lib/python2.7/encodings/utf_8.pyc matches /..../lib/python2.7/encodings/utf_8.py
import encodings.utf_8 # precompiled from /..../lib/python2.7/encodings/utf_8.pyc
Python 2.7.15 (default, May  7 2018, 17:08:03)
[GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
3
#   clean-up output omitted  

每个import ...行引用一个内置模块(Python二进制文件的一部分,用C实现)或.pyc字节码缓存文件。在脚本代码运行之前,有17个这样的文件被导入。你知道吗

主脚本中的3行代码转换为另外9个字节码指令:

>>> import dis
>>> dis.dis(compile(r'''\
... X = 1
... Y = 2
... print X + Y
... ''', '', 'exec'))
  2           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (X)

  3           6 LOAD_CONST               1 (2)
              9 STORE_NAME               1 (Y)

  4          12 LOAD_NAME                0 (X)
             15 LOAD_NAME                1 (Y)
             18 BINARY_ADD
             19 PRINT_ITEM
             20 PRINT_NEWLINE
             21 LOAD_CONST               2 (None)
             24 RETURN_VALUE

(我忽略了末尾的2个字节码,编码了一个额外的return None,它实际上不适用于一个模块)。你知道吗

相关问题 更多 >