Python函数背后的秘密(2)

函数对象是如何创建的?

程序运行时,虚拟机看见函数定义def语句,它都干些啥呢?

还是举一个简单例子吧,我们将一段代码放在字符串s中,然后用编译函数将它编译为字节码,再进行反汇编,就可以看到它的汇编指令了。

import dis

s = """
def foo(x,y='默认值',*,z='仅限关键字'):
    a = 1
"""

c = compile(s, "func", "exec")
dis.dis(c)

下面是用Python3.10.1运行的结果(不同版本的结果可能有差异):

  2           0 LOAD_CONST               6 (('默认值',))
              2 LOAD_CONST               1 ('仅限关键字')
              4 LOAD_CONST               2 (('z',))
              6 BUILD_CONST_KEY_MAP      1
              8 LOAD_CONST               3 (<code object foo ...)
             10 LOAD_CONST               4 ('foo')
             12 MAKE_FUNCTION            3 (defaults, kwdefaults)
             14 STORE_NAME               0 (foo)
             16 LOAD_CONST               5 (None)
             18 RETURN_VALUE

Disassembly of <code object foo at 0x00000263A4993940, file "func", line 2>:
  3           0 LOAD_CONST               1 (1)
              2 STORE_FAST               3 (a)
              4 LOAD_CONST               0 (None)
              6 RETURN_VALUE

反汇编后有两个代码对象,第一个代码对象是s编译后得到的模块级的代码对象c,第二个代码对象是函数foo的代码对象。def语句位于模块中,函数对象是在模块中创建的。

c.co_consts属性指令会用到,我们来看看其样子:

from pprint import pprint
pprint(c.co_consts)

('默认值',
 '仅限关键字',
 ('z',),
 <code object foo at 0x00000263A4993940, file "func", line 2>,
 'foo',
 None,
 ('默认值',))

对于模块级的代码对象c来说,co_names就是其局部变量名组成的元组:(‘foo’,)。

列出了这些属性,就比较容易看懂创建函数的这些个指令了:

LOAD_CONST 6 ((‘默认值’,))
把co_consts[6]即元组(‘默认值’,)压入栈

LOAD_CONST 1 (‘仅限关键字’)
把co_consts[1]即’仅限关键字’压入栈

LOAD_CONST 2 ((‘z’,))
把 co_consts[2]即键元组(‘z’,)压入栈

BUILD_CONST_KEY_MAP 1
将栈顶的前两项弹出,构建一个字典并压入栈

LOAD_CONST 3 (<code object foo …>)
把c.co_consts[6],即函数foo的代码块对象压入栈

LOAD_CONST 4 (‘foo’)
把c.co_consts[7],即函数名foo压入栈

MAKE_FUNCTION 3 (defaults, kwdefaults)
这条指令创建函数对象,看来秘密在这里:

  • 弹出栈中的函数名。
  • 弹出对应的foo代码块。
  • 创建函数对象,创建时把全局名字空间传递进去:
    • 为函数对象申请空间,创建函数对象。
    • 然后设置函数对象的成员属性。

MAKE_FUNCTION后面的标志(本指令为3即00000011),表示在栈帧中,代码对象foo的下面依次存储的是:

  • 仅限关键字构成的字典。
  • 默认值元组,位置参数和仅限位置参数的默认值,调用时可以用位置实参、关键字实参值来覆盖它们。

经过上面这些指令,把函数参数封装进了函数对象中了。后面两句是例行的返回指令。

                           未完待续
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!