注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

python符号表构建及符号查找  

2017-09-04 21:21:39|  分类: python |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、符号表的意义
其实在C++中,不同作用域的变量同样可能要生成不同的机器指令。例如,栈变量的访问就需要基于栈帧,而全局变量通产是一个链接时确定的绝对地址。同样,在python中,依然要识别出变量在什么作用域,并生成对应的虚拟机指令。但是和C++不同的一点是,python天然支持后向声明,例如对于下面语句:
tsecer@harry: cat post_decl.py
def use_post():
x = y + 1
y = 2
return x + z;
z = 10
tsecer@harry: python 
Python 2.6.6 (r266:84292, Nov 21 2013, 10:39:36) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis, post_decl
>>> dis.dis(post_decl.use_post.__code__)
  2           0 LOAD_FAST                0 (y)
              3 LOAD_CONST               1 (1)
              6 BINARY_ADD          
              7 STORE_FAST               1 (x)

  3          10 LOAD_CONST               2 (2)
             13 STORE_FAST               0 (y)

  4          16 LOAD_FAST                1 (x)
             19 LOAD_GLOBAL              0 (z)
             22 BINARY_ADD          
             23 RETURN_VALUE        
>>> 
在这里非常简单的例子中,可以看到,在use_post函数的开始,语句x = y + 1使用了后面才声明的函数变量y,但是在python生成虚拟机指令时,它依然正确的确定了它是一个局部变量,所以加载的时候使用的是 LOAD_FAST指令。这个虽然从语言使用者的角度来看自然而然,但是对于语言的实现者来说就至关重要了。这一点和C++类的声明相同,在声明中可以引用后面声明的变量(如果你曾经关注过这个问题的话,你应该会觉得这是一个挺特殊的语法,它事实上违反了常规的使用变量必须先要声明的规则),更具体的就是下面的例子:
tsecer@harry: cat class.post.cpp 
struct A
{
int get()
{
return m_x;
}
int m_x;
};
tsecer@harry: g++ -c class.post.cpp 
tsecer@harry: 
在A的get函数中,其实它使用的m_x并没有出现。所以编译器在对类进行解析的时候,同样需要首先禁掉常规的变量必须先声明的规则,而是为整个类建立完成的符号之后才进行真正的语法分析。

二、python对于一个导入模块的大致执行流程
1、模块的查找
这个日志大致打印了python查找一个模块所进行的搜索尝试,由于这里搜索的模块在系统中没有定义,所以这里列出了python完整的模块所有顺序及优先级。
tsecer@harry: cat mod_search.py 
import tsecer_not_exist
tsecer@harry: python -vv mod_search.py 2>&1 | grep tsecer_not_exist
# trying /home/tsecer/CodeTest/python.import.from/tsecer_not_exist.so
# trying /home/tsecer/CodeTest/python.import.from/tsecer_not_existmodule.so
# trying /home/tsecer/CodeTest/python.import.from/tsecer_not_exist.py
# trying /home/tsecer/CodeTest/python.import.from/tsecer_not_exist.pyc
# trying /usr/lib/python2.6/tsecer_not_exist.so
# trying /usr/lib/python2.6/tsecer_not_existmodule.so
# trying /usr/lib/python2.6/tsecer_not_exist.py
# trying /usr/lib/python2.6/tsecer_not_exist.pyc
# trying /usr/lib/python2.6/plat-linux2/tsecer_not_exist.so
# trying /usr/lib/python2.6/plat-linux2/tsecer_not_existmodule.so
# trying /usr/lib/python2.6/plat-linux2/tsecer_not_exist.py
# trying /usr/lib/python2.6/plat-linux2/tsecer_not_exist.pyc
# trying /usr/lib/python2.6/lib-dynload/tsecer_not_exist.so
# trying /usr/lib/python2.6/lib-dynload/tsecer_not_existmodule.so
# trying /usr/lib/python2.6/lib-dynload/tsecer_not_exist.py
# trying /usr/lib/python2.6/lib-dynload/tsecer_not_exist.pyc
# trying /usr/lib/python2.6/site-packages/tsecer_not_exist.so
# trying /usr/lib/python2.6/site-packages/tsecer_not_existmodule.so
# trying /usr/lib/python2.6/site-packages/tsecer_not_exist.py
# trying /usr/lib/python2.6/site-packages/tsecer_not_exist.pyc
# trying /usr/lib/python2.6/site-packages/gst-0.10/tsecer_not_exist.so
# trying /usr/lib/python2.6/site-packages/gst-0.10/tsecer_not_existmodule.so
# trying /usr/lib/python2.6/site-packages/gst-0.10/tsecer_not_exist.py
# trying /usr/lib/python2.6/site-packages/gst-0.10/tsecer_not_exist.pyc
# trying /usr/lib/python2.6/site-packages/gtk-2.0/tsecer_not_exist.so
# trying /usr/lib/python2.6/site-packages/gtk-2.0/tsecer_not_existmodule.so
# trying /usr/lib/python2.6/site-packages/gtk-2.0/tsecer_not_exist.py
# trying /usr/lib/python2.6/site-packages/gtk-2.0/tsecer_not_exist.pyc
# trying /usr/lib/python2.6/site-packages/webkit-1.0/tsecer_not_exist.so
# trying /usr/lib/python2.6/site-packages/webkit-1.0/tsecer_not_existmodule.so
# trying /usr/lib/python2.6/site-packages/webkit-1.0/tsecer_not_exist.py
# trying /usr/lib/python2.6/site-packages/webkit-1.0/tsecer_not_exist.pyc
    import tsecer_not_exist
ImportError: No module named tsecer_not_exist
tsecer@harry: 
2、运行一个模块的主要流程
Python-3.6.1\Python\pythonrun.c
static PyObject *
run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals,
            PyCompilerFlags *flags, PyArena *arena)
{
    PyCodeObject *co;
    PyObject *v;
    co = PyAST_CompileObject(mod, filename, flags, -1, arena);
    if (co == NULL)
        return NULL;
    v = PyEval_EvalCode((PyObject*)co, globals, locals);
    Py_DECREF(co);
    return v;
}
Python-3.6.1\Python\compile.c
PyCodeObject *
PyAST_CompileObject(mod_ty mod, PyObject *filename, PyCompilerFlags *flags,
                   int optimize, PyArena *arena)
{
……
    c.c_st = PySymtable_BuildObject(mod, filename, c.c_future);
    if (c.c_st == NULL) {
        if (!PyErr_Occurred())
            PyErr_SetString(PyExc_SystemError, "no symtable");
        goto finally;
    }

    co = compiler_mod(&c, mod);

 finally:
    compiler_free(&c);
    assert(co || PyErr_Occurred());
    return co;
}
完整的看这个执行流程,可以看到PySymtable_BuildObject是最早执行的,它早于虚拟机指令的生成(compiler_mod)。这一点的重要之处在于:当执行compiler_mod的时候,它已经可以知道完整的符号表信息,所以在生成对一个变量load/store的时候,就可以明确的知道使用栈变量还是全局变量的操作指令。
3、符号表对指令生成的影响
Python-3.6.1\Python\compile.c
static int
compiler_nameop(struct compiler *c, identifier name, expr_context_ty ctx)
{
……
    scope = PyST_GetScope(c->u->u_ste, mangled);
    switch (scope) {
    case FREE:
        dict = c->u->u_freevars;
        optype = OP_DEREF;
        break;
    case CELL:
        dict = c->u->u_cellvars;
        optype = OP_DEREF;
        break;
    case LOCAL:
        if (c->u->u_ste->ste_type == FunctionBlock)
            optype = OP_FAST;
        break;
    case GLOBAL_IMPLICIT:
        if (c->u->u_ste->ste_type == FunctionBlock)
            optype = OP_GLOBAL;
        break;
    case GLOBAL_EXPLICIT:
        optype = OP_GLOBAL;
        break;
    default:
        /* scope can be 0 */
        break;
    }
……
}

三、from x import y意味着什么
回过头来看下 form x import y这样的语句,它的神奇之处在于它的选择性范围非常精确,可以精确到一个模块中的任意一个符号中,这对于C++语言来说就有强烈的异域风情。在C++中,每个函数通过 static来控制自己的可见性,而不是使用者选择使用哪些内容。如果要精确控制导入一个模块的若干个符号,虚拟机需要如何实现呢?
1、导入对应的虚拟机指令
在下面的机器指令中,可以看到import执行之后,它有一个额外的STORE_NAME动作,这个动作会将import的内容放入当前函数的局部变量中,这样,import的符号就可以像其他本地声明的符号一样可以通过LOAD_NAME指令在局部变量列表中找到它。
tsecer@harry: cat base_import.py 
from dis import disassemble
import sys
tsecer@harry: python -m dis base_import.py
  1           0 LOAD_CONST               0 (-1)
              3 LOAD_CONST               1 (('disassemble',))
              6 IMPORT_NAME              0 (dis)
              9 IMPORT_FROM              1 (disassemble)
             12 STORE_NAME               1 (disassemble)
             15 POP_TOP             

  2          16 LOAD_CONST               0 (-1)
             19 LOAD_CONST               2 (None)
             22 IMPORT_NAME              2 (sys)
             25 STORE_NAME               2 (sys)
             28 LOAD_CONST               2 (None)
             31 RETURN_VALUE        
tsecer@harry: 
2、看下import之后函数执行帧中局部变量的内容
可以看到,import之后,sys和disassemble都作为局部变量存在了frame的f_locals中,从而可以被运行时查找到。
tsecer@harry: cat python_name_resolve.py 
from dis import disassemble
import sys

curframe = sys._getframe()

print "curframe.f_locals", curframe.f_locals

tsecer@harry: python python_name_resolve.py 
curframe.f_locals {'disassemble': <function disassemble at 0xb7757c34>, '__builtins__': <module '__builtin__' (built-in)>, 'curframe': <frame object at 0x81484e4>, '__file__': 'python_name_resolve.py', '__package__': None, 'sys': <module 'sys' (built-in)>, '__name__': '__main__', '__doc__': None}
tsecer@harry: 
3、import * 如何实现
对于这种暴力的整体导入语法,python在语法解析的时候就进行了特殊处理
tsecer@harry: cat python_name_resolve.py 
from dis import *
import sys

curframe = sys._getframe()

print "curframe.f_locals", curframe.f_locals

tsecer@harry: python -m dis python_name_resolve.py
  1           0 LOAD_CONST               0 (-1)
              3 LOAD_CONST               1 (('*',))
              6 IMPORT_NAME              0 (dis)
              9 IMPORT_STAR         
导入所有符号的结果
tsecer@harry: cat python_name_resolve.py 
from dis import *
import sys

curframe = sys._getframe()

print "curframe.f_locals", curframe.f_locals

tsecer@harry: python python_name_resolve.py 
curframe.f_locals {'HAVE_ARGUMENT': 90, 'haslocal': [124, 125, 126], 'opname': ['STOP_CODE', 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'DUP_TOP', 'ROT_FOUR', '<6>', '<7>', '<8>', 'NOP', 'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT', 'UNARY_CONVERT', '<14>', 'UNARY_INVERT', '<16>', '<17>', 'LIST_APPEND', 'BINARY_POWER', 'BINARY_MULTIPLY', 'BINARY_DIVIDE', 'BINARY_MODULO', 'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_SUBSCR', 'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE', 'INPLACE_FLOOR_DIVIDE', 'INPLACE_TRUE_DIVIDE', 'SLICE+0', 'SLICE+1', 'SLICE+2', 'SLICE+3', '<34>', '<35>', '<36>', '<37>', '<38>', '<39>', 'STORE_SLICE+0', 'STORE_SLICE+1', 'STORE_SLICE+2', 'STORE_SLICE+3', '<44>', '<45>', '<46>', '<47>', '<48>', '<49>', 'DELETE_SLICE+0', 'DELETE_SLICE+1', 'DELETE_SLICE+2', 'DELETE_SLICE+3', 'STORE_MAP', 'INPLACE_ADD', 'INPLACE_SUBTRACT', 'INPLACE_MULTIPLY', 'INPLACE_DIVIDE', 'INPLACE_MODULO', 'STORE_SUBSCR', 'DELETE_SUBSCR', 'BINARY_LSHIFT', 'BINARY_RSHIFT', 'BINARY_AND', 'BINARY_XOR', 'BINARY_OR', 'INPLACE_POWER', 'GET_ITER', '<69>', 'PRINT_EXPR', 'PRINT_ITEM', 'PRINT_NEWLINE', 'PRINT_ITEM_TO', 'PRINT_NEWLINE_TO', 'INPLACE_LSHIFT', 'INPLACE_RSHIFT', 'INPLACE_AND', 'INPLACE_XOR', 'INPLACE_OR', 'BREAK_LOOP', 'WITH_CLEANUP', 'LOAD_LOCALS', 'RETURN_VALUE', 'IMPORT_STAR', 'EXEC_STMT', 'YIELD_VALUE', 'POP_BLOCK', 'END_FINALLY', 'BUILD_CLASS', 'STORE_NAME', 'DELETE_NAME', 'UNPACK_SEQUENCE', 'FOR_ITER', '<94>', 'STORE_ATTR', 'DELETE_ATTR', 'STORE_GLOBAL', 'DELETE_GLOBAL', 'DUP_TOPX', 'LOAD_CONST', 'LOAD_NAME', 'BUILD_TUPLE', 'BUILD_LIST', 'BUILD_MAP', 'LOAD_ATTR', 'COMPARE_OP', 'IMPORT_NAME', 'IMPORT_FROM', '<109>', 'JUMP_FORWARD', 'JUMP_IF_FALSE', 'JUMP_IF_TRUE', 'JUMP_ABSOLUTE', '<114>', '<115>', 'LOAD_GLOBAL', '<117>', '<118>', 'CONTINUE_LOOP', 'SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY', '<123>', 'LOAD_FAST', 'STORE_FAST', 'DELETE_FAST', '<127>', '<128>', '<129>', 'RAISE_VARARGS', 'CALL_FUNCTION', 'MAKE_FUNCTION', 'BUILD_SLICE', 'MAKE_CLOSURE', 'LOAD_CLOSURE', 'LOAD_DEREF', 'STORE_DEREF', '<138>', '<139>', 'CALL_FUNCTION_VAR', 'CALL_FUNCTION_KW', 'CALL_FUNCTION_VAR_KW', 'EXTENDED_ARG', '<144>', '<145>', '<146>', '<147>', '<148>', '<149>', '<150>', '<151>', '<152>', '<153>', '<154>', '<155>', '<156>', '<157>', '<158>', '<159>', '<160>', '<161>', '<162>', '<163>', '<164>', '<165>', '<166>', '<167>', '<168>', '<169>', '<170>', '<171>', '<172>', '<173>', '<174>', '<175>', '<176>', '<177>', '<178>', '<179>', '<180>', '<181>', '<182>', '<183>', '<184>', '<185>', '<186>', '<187>', '<188>', '<189>', '<190>', '<191>', '<192>', '<193>', '<194>', '<195>', '<196>', '<197>', '<198>', '<199>', '<200>', '<201>', '<202>', '<203>', '<204>', '<205>', '<206>', '<207>', '<208>', '<209>', '<210>', '<211>', '<212>', '<213>', '<214>', '<215>', '<216>', '<217>', '<218>', '<219>', '<220>', '<221>', '<222>', '<223>', '<224>', '<225>', '<226>', '<227>', '<228>', '<229>', '<230>', '<231>', '<232>', '<233>', '<234>', '<235>', '<236>', '<237>', '<238>', '<239>', '<240>', '<241>', '<242>', '<243>', '<244>', '<245>', '<246>', '<247>', '<248>', '<249>', '<250>', '<251>', '<252>', '<253>', '<254>', '<255>'], 'hasfree': [135, 136, 137], 'hascompare': [106], '__package__': None, 'hasjabs': [113, 119], '__doc__': None, 'hasjrel': [93, 110, 111, 112, 120, 121, 122], 'hasconst': [100], 'disassemble': <function disassemble at 0xb76ccc34>, '__builtins__': <module '__builtin__' (built-in)>, 'curframe': <frame object at 0x82594e4>, '__file__': 'python_name_resolve.py', 'sys': <module 'sys' (built-in)>, '__name__': '__main__', 'EXTENDED_ARG': 143, 'distb': <function distb at 0xb76ccbc4>, 'disco': <function disassemble at 0xb76ccc34>, 'opmap': {'CALL_FUNCTION': 131, 'DUP_TOP': 4, 'INPLACE_FLOOR_DIVIDE': 28, 'BINARY_XOR': 65, 'END_FINALLY': 88, 'RETURN_VALUE': 83, 'POP_BLOCK': 87, 'SETUP_LOOP': 120, 'POP_TOP': 1, 'EXTENDED_ARG': 143, 'SETUP_FINALLY': 122, 'INPLACE_TRUE_DIVIDE': 29, 'CALL_FUNCTION_KW': 141, 'INPLACE_AND': 77, 'SETUP_EXCEPT': 121, 'STORE_NAME': 90, 'IMPORT_NAME': 107, 'LOAD_GLOBAL': 116, 'LOAD_NAME': 101, 'FOR_ITER': 93, 'EXEC_STMT': 85, 'DELETE_NAME': 91, 'BUILD_LIST': 103, 'COMPARE_OP': 106, 'BINARY_OR': 66, 'INPLACE_MULTIPLY': 57, 'STORE_FAST': 125, 'CALL_FUNCTION_VAR': 140, 'LOAD_LOCALS': 82, 'CONTINUE_LOOP': 119, 'PRINT_EXPR': 70, 'DELETE_GLOBAL': 98, 'GET_ITER': 68, 'STOP_CODE': 0, 'UNARY_NOT': 12, 'BINARY_LSHIFT': 62, 'JUMP_IF_TRUE': 112, 'LOAD_CLOSURE': 135, 'IMPORT_STAR': 84, 'INPLACE_OR': 79, 'BINARY_SUBTRACT': 24, 'STORE_MAP': 54, 'INPLACE_ADD': 55, 'INPLACE_LSHIFT': 75, 'INPLACE_MODULO': 59, 'STORE_ATTR': 95, 'BUILD_MAP': 104, 'BINARY_DIVIDE': 21, 'INPLACE_RSHIFT': 76, 'PRINT_ITEM_TO': 73, 'UNPACK_SEQUENCE': 92, 'BINARY_MULTIPLY': 20, 'MAKE_FUNCTION': 132, 'PRINT_NEWLINE_TO': 74, 'NOP': 9, 'LIST_APPEND': 18, 'INPLACE_XOR': 78, 'STORE_GLOBAL': 97, 'INPLACE_SUBTRACT': 56, 'INPLACE_POWER': 67, 'ROT_FOUR': 5, 'DELETE_SUBSCR': 61, 'BINARY_AND': 64, 'BREAK_LOOP': 80, 'JUMP_IF_FALSE': 111, 'DELETE_SLICE+1': 51, 'DELETE_SLICE+0': 50, 'DUP_TOPX': 99, 'CALL_FUNCTION_VAR_KW': 142, 'LOAD_ATTR': 105, 'BINARY_TRUE_DIVIDE': 27, 'ROT_TWO': 2, 'IMPORT_FROM': 108, 'DELETE_FAST': 126, 'BINARY_ADD': 23, 'LOAD_CONST': 100, 'STORE_DEREF': 137, 'UNARY_NEGATIVE': 11, 'UNARY_POSITIVE': 10, 'STORE_SUBSCR': 60, 'BUILD_TUPLE': 102, 'BINARY_POWER': 19, 'BUILD_CLASS': 89, 'UNARY_CONVERT': 13, 'BINARY_MODULO': 22, 'DELETE_SLICE+3': 53, 'DELETE_SLICE+2': 52, 'WITH_CLEANUP': 81, 'DELETE_ATTR': 96, 'PRINT_ITEM': 71, 'RAISE_VARARGS': 130, 'SLICE+0': 30, 'SLICE+1': 31, 'SLICE+2': 32, 'SLICE+3': 33, 'LOAD_DEREF': 136, 'LOAD_FAST': 124, 'BINARY_FLOOR_DIVIDE': 26, 'BINARY_RSHIFT': 63, 'BINARY_SUBSCR': 25, 'YIELD_VALUE': 86, 'ROT_THREE': 3, 'STORE_SLICE+0': 40, 'STORE_SLICE+1': 41, 'STORE_SLICE+2': 42, 'STORE_SLICE+3': 43, 'UNARY_INVERT': 15, 'PRINT_NEWLINE': 72, 'INPLACE_DIVIDE': 58, 'BUILD_SLICE': 133, 'JUMP_ABSOLUTE': 113, 'MAKE_CLOSURE': 134, 'JUMP_FORWARD': 110}, 'cmp_op': ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is not', 'exception match', 'BAD'), 'hasname': [90, 91, 95, 96, 97, 98, 101, 105, 107, 108, 116], 'dis': <function dis at 0xb76ccd4c>}
tsecer@harry: 

  评论这张
 
阅读(47)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017