设为首页 收藏本站
查看: 770|回复: 0

[经验分享] [笔记]Python虚拟机的运行时基本知识

[复制链接]

尚未签到

发表于 2017-5-5 10:04:09 | 显示全部楼层 |阅读模式
首先应该了解程序的运行时刻环境,个人觉得龙书中文版第7章挺通俗易懂的。



Python在这方面设计了PyFrameObject这个结构(对应于龙书中的“活动记录”)来维护运行时环境,并采用了“访问链”的思想(龙书中介绍了“访问链”和“显示表”)来解决不同作用域间变量的访问问题。

不过在PyFrameObject中维护了3个成员,用来指向最经常使用的3个符号表,内置符号表、全局符号表、局部符号表:

    PyObject *f_builtins;     /* builtin symbol table (PyDictObject) */
PyObject *f_globals;      /* global symbol table (PyDictObject) */
PyObject *f_locals;       /* local symbol table (any mapping) */




这样可以避免在访问全局变量、内建变量时还要通过“访问链”上的回溯来搜索。

PyFrameObject通过如下成员来维护“访问链”(或者称“符号表链”、“名字空间链”):

struct _frame *f_back;    /* previous frame, or NULL */

关于Python的作用域,有一些规则。


最内嵌套作用域规则:由一个赋值语句引进的名字在这个赋值语句所在的作用域里是可见(起作用)的,而且在其内部嵌套的每个作用域里也可见,除非它被嵌套于内部的,引进同样名字的另一条赋值语句所遮蔽/覆盖。

LEGB:符号表的搜索顺序是Local -> Enclosing Function -> Global -> Built-in




一个比较常见而且经典的案例是UnboundLocalError,见如下代码:

x = 10
def foo():
print(x)
x += 1
foo()

这一段代码会出现如下错误:


UnboundLocalError: local variable 'x' referenced before assignment




这个问题可以用下面两段话来解答:

This is because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value
tox, the compiler recognizes it as a local variable. Consequently when the earlierprintxattempts
to print the uninitialized local variable and an error results.


URL:http://docs.python.org/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

Otherwise, all variables found outside of the innermost scope areread-only(an attempt to write to such a variable will simply create anewlocal
variable in the innermost scope, leaving the identically named outer variable unchanged).


URL:http://docs.python.org/tutorial/classes.html#python-scopes-and-namespaces




第二个URL,即官方文档也说明了LEGB规则:


  • the innermost scope, which is searched first, contains the local names
  • the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
  • the next-to-last scope contains the current module’s global names
  • the outermost scope (searched last) is the namespace containing built-in names








上面讨论了帧对象PyFrameObject和作用域、符号表等,下面是比较大的概念:关于Python虚拟机的运行时环境。




虚拟机的具体实现位于ceval.c中的PyEval_EvalFrameEx函数中。

函数开头首先定义了如下变量:

    register PyObject **stack_pointer;  /* Next free slot in value stack */
register unsigned char *next_instr;
register int opcode;        /* Current opcode */
register int oparg;         /* Current opcode argument, if any */
register enum why_code why; /* Reason for block stack unwind */

含义可以从注释中看出,比如next_instr表示下一条指令,why表示栈展开的原因。





PyEval_EvalFrameEx是一个非常庞大的函数,拥有庞大的switch/case语句数目来执行各种指令。

函数中提供了几个访问指令的宏:

/* Code access macros */
#define INSTR_OFFSET()  ((int)(next_instr - first_instr))
#define NEXTOP()        (*next_instr++)
#define NEXTARG()       (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2])
#define PEEKARG()       ((next_instr[2]<<8) + next_instr[1])
#define JUMPTO(x)       (next_instr = first_instr + (x))
#define JUMPBY(x)       (next_instr += (x))

此外,在运行时需要涉及的还有线程和进程,Python使用的是系统原生的线程/进程,并使用PyThreadState和PyInterpreterState对象来进行抽象和维护。


在PyEval_EvalFrameEx函数开头,也定义了tstate变量,并把当前线程状态赋值给该变量:

PyThreadState *tstate = PyThreadState_GET();

接着设置线程状态对象中的帧:


    tstate->frame = f;

然后再设置帧的一些信息:


    co = f->f_code;
names = co->co_names;
consts = co->co_consts;
fastlocals = f->f_localsplus;
freevars = f->f_localsplus + co->co_nlocals;
first_instr = (unsigned char*) PyString_AS_STRING(co->co_code);
next_instr = first_instr + f->f_lasti + 1;
stack_pointer = f->f_stacktop;
assert(stack_pointer != NULL);
f->f_stacktop = NULL;       /* remains NULL unless yield suspends frame */

最后,进入switch/case:


        switch (opcode) {

P.S. “访问链”的形成是在PyFrame_New函数中,帧的f_back成员指向当前线程状态对象的frame成员。






JasonLee   2011.08.20   20:18

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-373310-1-1.html 上篇帖子: 启用 VIM 中的 Python 自动补全及提示功能 下篇帖子: 序列元素Python入门笔记(5):重要的序列
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表