c语言编程笔录

您现在的位置是:首页 > 编程 > 编程经验

编程经验

Python虚拟机字节码之控制流如何实现

夏志豪 2023-09-08编程经验
Python虚拟机字节码之控制流实现Python虚拟机采用了一种类栈帧结构,即为每个函数调用创建一个栈帧,每个栈帧包含了函数的参数、局部变量以及函数的执行状态,如程序计数器、返回地址等。栈帧以后入先出

Python虚拟机字节码之控制流实现

Python虚拟机采用了一种类栈帧结构,即为每个函数调用创建一个栈帧,每个栈帧包含了函数的参数、局部变量以及函数的执行状态,如程序计数器、返回地址等。栈帧以后入先出的方式存放在虚拟机的栈中,形成了调用链。 Python解释器每次只会执行栈顶的栈帧,栈顶栈帧执行完毕后再弹出并销毁,然后继续执行下一个栈帧。控制流是由字节码指令来实现的。

1、条件跳转

条件跳转主要利用了jumps指令的分支作用。jumps指令专门用于在代码流中跳转。 当某条件成立时,就通过jumps调到指定位置开始执行指定的代码块。如果条件不成立,就可以继续往下执行。示例代码如下:

def compare(a,b):
    if a>b:
        return "greater"
    elif a==b:
        return "equal"
    else:
        return "less"

对应的字节码如下:

2 LOAD_FAST      0 (a)
5 LOAD_FAST      1 (b)
8 COMPARE_OP     4 (>)

11 POP_JUMP_IF_FALSE  20

14 LOAD_CONST     0 ('greater')
17 RETURN_VALUE

18 JUMP_FORWARD   11 (to 32)

20 LOAD_FAST      0 (a)
23 LOAD_FAST      1 (b)
26 COMPARE_OP     2 (==)

29 POP_JUMP_IF_FALSE  38

32 LOAD_CONST     1 ('equal')
35 RETURN_VALUE

36 JUMP_FORWARD    9 (to 48)

38 LOAD_CONST     2 ('less')
41 RETURN_VALUE

44 JUMP_FORWARD    1 (to 48)

2、循环

循环是由jump指令来实现的,包括while、for等,可以使用jumps指令实现循环开始和循环结束条件,从而执行相应的代码块。示例代码如下:

def squareList(n):
    result = []
    for i in range(n):
        result.append(i*i)
    return result

对应的字节码如下:

 2 BUILD_LIST        0
 5 LOAD_GLOBAL       0 (range)
 8 LOAD_FAST         0 (n)
11 CALL_FUNCTION     1
14 GET_ITER
15 FOR_ITER        28 (to 46)

18 STORE_FAST        1 (i)
21 LOAD_FAST         1 (i)
24 LOAD_FAST         1 (i)
27 BINARY_MULTIPLY
28 LIST_APPEND      2
31 JUMP_ABSOLUTE    15

3、异常处理

Python支持try/except语句块来处理异常,字节码内部提供了一些指令来支持异常处理。try块包含了可能产生异常的代码,当代码块中发生异常时,就会由对应的exception handler代码来处理异常。对于try/except外的其他代码,也需要设置对应的exception handler,以免出现未处理的异常。示例代码如下:

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as e:
    print("I/O error occurred: ", format(e.errno, e.strerror))
except ValueError:
    print("Could not convert data to an integer.")
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise
finally:
    f.close()

对应的字节码如下:

  2 SETUP_FINALLY      14 (to 19)

  5 LOAD_GLOBAL        0 (open)
  8 LOAD_CONST         1 ('myfile.txt')
 11 CALL_FUNCTION      1
 14 STORE_FAST         0 (f)

 17 JUMP_FORWARD       2 (to 22)

 20 POP_BLOCK

 21 POP_EXCEPT

 22 LOAD_FAST          0 (f)
 25 LOAD_ATTR          1 (readline)
 28 CALL_FUNCTION      0
 31 STORE_FAST         1 (s)

 34 LOAD_GLOBAL        2 (int)
 37 LOAD_FAST          1 (s)
 40 LOAD_ATTR          3 (strip)
 43 CALL_FUNCTION      0
 46 CALL_FUNCTION      1
 49 STORE_FAST         2 (i)

 52 JUMP_FORWARD        2 (to 57)

 55 POP_BLOCK

 56 POP_EXCEPT

 57 LOAD_FAST          0 (f)
 60 LOAD_ATTR          4 (close)
 63 CALL_FUNCTION      0
 66 POP_BLOCK

4、函数调用

函数调用是字节码的基本操作之一,Python虚拟机为每个函数调用创建一个新的栈帧,并将其入栈。调用将转移到新的栈帧,该栈帧在调用完成后将被移出栈。除了依次将函数的参数推入栈中,函数调用时还必须为函数分配新的帧对象。针对不同的数据类型,Python虚拟机还提供了相应的函数调用指令,如CALL_FUNCTION、RETURN_VALUE等。示例代码如下:

def greet(name):
    return "Hello " + name

def add(x, y):
    return x + y

greeting = greet("John")
result = add(2, 3)
print(greeting)
print(result)

对应的字节码如下:

 2 LOAD_CONST           1 ('John')
 5 CALL_FUNCTION        1
 8 STORE_FAST           1 (greeting)

11 LOAD_CONST           2 (2)
14 LOAD_CONST           3 (3)
17 BINARY_ADD
18 RETURN_VALUE

文章评论