[笔记]Python虚拟机对函数的解释

demo.py:

i = 1

def f():
    j = 2

f()

test.py:
import dis

source = open('./demo.py').read()
co = compile(source, './demo.py', 'exec')
print(co.co_name)
dis.dis(co)

print("\n")
fCode = co.co_consts[1]
print(fCode.co_name)
dis.dis(fCode)

输出:
<module>
  1           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (i)

  3           6 LOAD_CONST               1 (<code object f at 0x022F5430, file "./demo.py", line 3>)
              9 MAKE_FUNCTION            0
             12 STORE_NAME               1 (f)

  6          15 LOAD_NAME                1 (f)
             18 CALL_FUNCTION            0
             21 POP_TOP             
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE        


f
  4           0 LOAD_CONST               1 (2)
              3 STORE_FAST               0 (j)
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE  

对于def f():这一语句,Python虚拟机先将函数f对应的PyCodeObject入栈,然后再MAKE_FUNCTION。
        case MAKE_FUNCTION:
            v = POP(); /* code object */
            x = PyFunction_New(v, f->f_globals);
            Py_DECREF(v);
            /* XXX Maybe this should be a separate opcode? */
            if (x != NULL && oparg > 0) {
                v = PyTuple_New(oparg);
                if (v == NULL) {
                    Py_DECREF(x);
                    x = NULL;
                    break;
                }
                while (--oparg >= 0) {
                    w = POP();
                    PyTuple_SET_ITEM(v, oparg, w);
                }
                err = PyFunction_SetDefaults(x, v);
                Py_DECREF(v);
            }
            PUSH(x);
            break;

在相应代码中,将对应的PyCodeObject和帧的全局符号表指针传递给PyFunction_New函数,调用之,最后将返回值入栈。
在PyFunction_New函数中,创建一个PyFunctionObject,然后初始化相应的成员信息,返回之。

而对于函数f的调用,首先将函数名入栈,再执行CALL_FUNCTION:
        case CALL_FUNCTION:
        {
            PyObject **sp;
            PCALL(PCALL_ALL);
            sp = stack_pointer;
#ifdef WITH_TSC
            x = call_function(&sp, oparg, &intr0, &intr1);
#else
            x = call_function(&sp, oparg);
#endif
            stack_pointer = sp;
            PUSH(x);
            if (x != NULL)
                continue;
            break;
        }

在这里,将栈顶指针和参数信息传递给函数call_function,然后恢复栈顶指针,将返回值入栈。
最后实际上是创建了一页帧,然后以帧作为活动环境,调用PyEval_EvalFrameEx函数。
        f = PyFrame_New(tstate, co, globals, NULL);
        if (f == NULL)
            return NULL;

        fastlocals = f->f_localsplus;
        stack = (*pp_stack) - n;

        for (i = 0; i < n; i++) {
            Py_INCREF(*stack);
            fastlocals[i] = *stack++;
        }
        retval = PyEval_EvalFrameEx(f,0);



JasonLee     2011.08.27     19:52
上一篇:FreeSwitch 的初始化及其模块加载过程


下一篇:拼多多面试题:如何用 Redis 统计独立用户访问量?