python虚拟机

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python虚拟机相关的知识,希望对你有一定的参考价值。

翻译自《Python Virtual Machine》

Python 虚拟机
 
每个函数对象都和以下的三个结构:
1。包含参数的局部变量名称(in .__code__.varnames)
2。全局变量名称(in .__code__.co_names)
3。常数(in .__code__.co_consts)
 
在python定义函数的时候创建这些结构,它们被定义在函数对应的__code__对象。
 
如果我们定义如下:
Def minimum(alist):
    m=None if let(alist) ==0 else Alist[0]
    for v in alist[1:]:
        if vim:
            m = v
    return m
 
我们得到
minimum.__code__.co_varnames is (‘alist‘,‘m‘,‘v‘)
minimum.__code__.co_names is (‘len‘,‘None‘)
minimum.__code__.co_consts is (None,0,1)
 
用于索引的数字+load 运算符(LOAD_FAST、LOAD_GLOBAL、LOAD_CONST都会在之后讨论)。
 
在PVM中主要的数据结构式“regular”栈(由一连串的push、pop组成)。对栈的主要操作就是load/push和store/pop。我们在栈顶load/push一个值,栈向上扩展,伴随着栈指针上移指向栈顶。同样,store/pop一个栈顶值时,栈指针下移。
 
还有一个次要的block栈用于从循环嵌套、try和指令中取出值。比如,一个断点指令在block栈中被编码,用于判断哪个循环块被n断下(并如何继续执行循环外的指令)。当循环,try/except和指令开始执行时,他们的信息被push到block栈上;当它们结束时从堆栈上弹出。这种块block stack对于现在来说太过麻烦,不必要去理解:所以当我们遇到有关block stack 的指令时,会指出将其忽略的原因。
 
这儿有个有关栈操作的简单例子,计算 d=a+b*c。假设a、b、c、d都是一个函数中的局部变量:co_varnames =(‘a‘,‘b‘,‘c‘,‘d‘)且这些符号对应的实际值被存放在并行元组中:(1,2,3,none)。符号在元组中的位置与其值在元组的位置是一一对应的。
 
LOAD_FAST N
load/push 将co_varnames[N]对应的值压入栈,stackp+=1,stack[stackp] = co_varnames[N]
 
STORE_FAST N
store/pop 将栈顶的值放入co_varnames[N], co_varnames[N] = stack[stackp], stackp-=1
 
BINARY_MULTIPLY
将‘*’的两个运算数压入栈,stack[stackp-1]=stack[stackp-1]*stack[stack];stackp-=1(将栈顶的两个值转化为它们的乘积)
 
BINARY_ADD
将‘+’的两个运算数压入栈,stack[stackp-1]=stack[stackp-1]+stack[stack];stackp-=1(将栈顶的两个值转化为它们的和)
 
d = a+b*c  的PVM code:
LOAD_FAST 0
LOAD_FAST 1
LOAD_FAST 2
BINARY_MULTIPLY
BINARY_ADD
STORE_FAST 3
 
 
初始状态:
co_varnames =(‘a‘,‘b‘,‘c‘,‘d‘)
values=(1,2,3,none)
     +--------------------+
3    |                    |
     +--------------------+
2    |                    | 
     +--------------------+
1    |                    | 
     +--------------------+
0    |                    |
     +--------------------+
stack (with stackp=-1,it is an empty stack)
 
LOAD_FAST 0:
     +--------------------+
3    |                    |
     +--------------------+
2    |                    | 
     +--------------------+
1    |                    | 
     +--------------------+
0    |     1: value of a  |
     +--------------------+
stack(with stackp=0)
 
LOAD_FAST 1:
     +--------------------+
3    |                    |
     +--------------------+
2    |                    | 
     +--------------------+
1    |    2: value of b  | 
     +--------------------+
0    |    1: value of a  |
     +--------------------+
stack (with stackp=1)
 
LOAD_FAST 2:
     +--------------------+
3    |                    |
     +--------------------+
2    |   3: value of c   | 
     +--------------------+
1    |    2: value of b  | 
     +--------------------+
0    |    1: value of a  |
     +--------------------+
stack (with stackp=2)
 
BINARY_MULTIPLY:
     +--------------------+
3    |                    |
     +--------------------+
2    |                    | 
     +--------------------+
1    |   6: value of b*c | 
     +--------------------+
0    |   1: value of a   |
     +--------------------+
stack (with stackp=1)
 
BINARY_ADD:
     +--------------------+
3    |                    |
     +--------------------+
2    |                    | 
     +--------------------+
1    |                    | 
     +--------------------+
0    | 7: value of a+b*c |
     +--------------------+
stack (with stackp=0)
 
STORE_FAST 3:
     +--------------------+
3    |                    |
     +--------------------+
2    |                    | 
     +--------------------+
1    |                    | 
     +--------------------+
0    |                    |
     +--------------------+
stack (with stackp=-1)
co_varnames =(‘a‘,‘b‘,‘c‘,‘d‘)
values=(1,2,3,7)
 
 
PVM的控制流
 
在PVM的每个指令都包含了1~3字节的信息。第一个字节是操作标识或字节码,后面的两字节是字节码的操作数(但并不是所有的字节码都需要操作数:BINARY_ADD就不需要)。两字节能够表示0~65536:所以python的函数中不能有超过65536个不同的局部变量。
 
指令被储存在内存中:把内存也看作一种储存有次序的数据的列表结构。
Memory          Instruction
Location  
0               LOAD_FAST 0
3               LOAD_FAST 1
6               LOAD_FAST 2
9               BINARY_MULTIPLY
10              BINARY_ADD
11              STORE_FAST 3
把内存列表命名为m
第一条指令被存储在m[0],后一指令存储在高3或高1的位置处(占3字节:有些指令有明确操作数的:load/store。有些指令有隐含的操作数:stack 、pc。占1字节:没有操作数的指令:binary运算)
 
一旦这些指令被加载进内存后,PVM按照一个简单的规则执行他们。执行周期赋予了计算机生命,这是计算机科学的基础。
(1)从m [pc]开始获取操作及其操作数(如果存在)
(2)pc + = 3(如果操作数存在)或pc + = 1(如果没有操作数存在)
(3)执行操作码(可能更改其操作数,堆栈,堆栈或pc)
(4)转到步骤1
 
一些运算会操作stack/stackp和存变量值的元组,一些会改变pc(比如jump指令)。
所以pc初始时0,PVM执行上述代码以以下流程:
  1.获取操作m [0],操作数m [1]和m [2]
  2.将pc递增至3
  3.操纵堆栈(见上文)
  4.回到步骤1
 
  1.取m [3]的操作,m [4]和m [5]
  2.将pc增加到6
  3.操纵堆栈(见上文)
  4.回到步骤1
 
  1.取m [6]和m [7]和m [8]的操作数,
  2.将pc增加到9
  3.操纵堆栈(见上文)
  4.回到步骤1
 
  1.获取操作a m [9]:它没有操作数
  2.将pc增加到10
  3.操纵堆栈(见上文)
  4.回到步骤1
 
  1.获取操作m [10]:它没有操作数
  2.将pc增加到11
  3.操纵堆栈(见上文)
  4.回到步骤1
 
内存中指向此处时,没有代码可以执行。在下一个例子中我们可以看到PVM如何执行一个更复杂的代码。
 
如简要介绍的那样,我们可以用dis.py模块中使用dis函数打印任何Python函数(和模块/类也可以)的注释描述;这里我们打印函数。 
 
def addup(alist):
     sum=0
     for v in alist:
          sum = sum + v
     return sum

  

 
这个例子用来显示一般函数对象的有用的信息(它的名称,它的三个元组,和反编译信息)
 
def func_obj(fo):
     print(fo.__name__)
     print(‘  co_varnames:‘,fo.__code__.co_varnames)
     print(‘  co_names   :‘,fo.__code__.co_names)
     print(‘  co_consts  :‘,fo.__code__.co_consts,‘\n‘)
     print(‘Source Line m operation/byte-code   operand (useful name/number)\n‘+69*‘-‘)
     dis.dis(fo)
 
calling func_obj(addup) prints
 
addup
co_varnames: (‘alist‘, ‘sum‘, ‘v‘)
co_names : ()
co_consts : (None, 0)
Source Line m      op/byte-code  operand (useful name/number)
---------------------------------------------------------------------
2           0      LOAD_CONST    1 (0)
            3      STORE_FAST    1 (sum)
 
3           6      SETUP_LOOP    24 (to 33)
            9      LOAD_FAST     0 (alist)
            12     GET_ITER
         >> 13     FOR_ITER      16 (to 32)
            16     STORE_FAST    2 (v)
 
4           19     LOAD_FAST     1 (sum)
            22     LOAD_FAST     2 (v)
            25     BINARY_ADD
            26     STORE_FAST    1 (sum)
            29     JUMP_ABSOLUTE 13
         >> 32     POP_BLOCK
 
5        >> 33     LOAD_FAST     1 (sum)
            36     RETURN_VALUE
 

  

 
有>>标识的行说明有其他指令会jump到此行。
 
更详细的描述:
第2行:
 m [0]:在堆栈上加载值0(co_consts [1])
 m [3]:将值0存入sum(co_varnames [1])
 
第3行:
 m [6]:通过将循环的大小推到块堆栈来设置循环
           (回想一下,我们不会对块堆栈做任何事情)
 m [9]:在堆栈中加载alist(co_varnames [0])的值
 m [12]:通过迭代器替换堆栈上的值(通过弹出和推送)
 m [13]:在堆栈中加载下一个迭代器值,如果StopIteration引起,则跳转到m [32]
           (m [29]中的代码跳回此位置进行循环)
 m [16]:将下一个值存储到v(co_varnames [2])中,将其从堆栈中弹出
 
第4行:
 m [19]:在堆栈中加载sum(co_varnames [1])的值
 m [22]:将v(co_varnames [2])的值加载到堆栈上
 m [25]:从堆栈中删除/添加两个值,将结果推送到堆栈
 m [26]:将值存储在sum(co_varnames [1])中,将其从堆栈中弹出
 m [29]:将pc设置为13,因此在m [13]中执行的下一条指令
             (跳回到前一个位置使循环循环)
 m [32]:弹出什么m [6]推到块堆栈上
             (回想一下,我们不会对块堆栈做任何事情)
             (m [13]中的代码在这里跳转到StopIteration,终止循环)
 
第5行:
 m [33]:加载堆栈上的sum(co_varnames [1])的值返回
 m [36]:从函数返回结果在堆栈顶部

以上是关于python虚拟机的主要内容,如果未能解决你的问题,请参考以下文章

Python虚拟机框架

python在windows创建虚拟机环境

Python虚拟机函数机制之位置参数

python环境搭建-pycharm远程环境代码同步配置

Python虚拟机框架

python调用java代码 java虚拟机(jvm)