python function was the implementation process

For Python conventional function, there is only one entrance, but there will be more export returns as return or throw an exception. Function from the entrance will run until the return statement or throw an exception, not the middle pause function has always had control. When the end of the run, it returns control to the caller.

Previously introduced, when executing Python code, the code will be compiled into byte code first, then interpreted byte code in a virtual machine, the compiled byte code is saved in a file or .pyd .pyc extension in . At run time, the opportunity to create a virtual context byte code execution, simulation runtime stack Python C language as the runtime environment, use the run-time stack PyFrameObject representation, and PyCodeObject object byte code is stored in.

Python is a stack-based interpreter, wherein there are three stacks: call stack (frame stack), data stack (data stack), the block stack (block statck).

PyFrameObject present in the call stack, wherein the field on a point f_back PyFrameObject, thus forming a chain of calls. Each call stack once the corresponding function call. Call stack have their own stack and data stack block, the data will be stored in the stack data bytes of operations, the stack for a particular control block stream block, such as loops and exception handling.

Open the terminal, enter the command python3or the ipythonopen command line interactive Python command interpreter:

If you use ipython to be installed in advance, you need under Python 3 environments.

pip3 intall ipython
import inspect

# 全局变量
a = 0
x, y = None, None

def fun1():
    b = 1       # 定义局部变量
    global x    # 将变量 x 设为全局变量,因为它会在函数内部被修改
    # inspect 获取当前栈帧,相当于 PyFrameObject 对象
    x = inspect.currentframe()
    # 打印当前栈帧中运行的行号
    print(x.f_lasti)
    print('running fun1')
    return b

def fun2(d):
    # 局部变量赋值
    c = a
    e = d
    print('running fun2')
    # 调用方法
    fun1()
    global y
    # 获取当前栈帧
    y = inspect.currentframe()
    f = 2
    return f

import dis

# dis 方法查看函数的字节码
>>> dis.dis(fun2)

  2           0 LOAD_GLOBAL              0 (a)
              2 STORE_FAST               1 (c)

  3           4 LOAD_FAST                0 (d)
              6 STORE_FAST               2 (e)

  4           8 LOAD_GLOBAL              1 (print)
             10 LOAD_CONST               1 ('running fun2')
             12 CALL_FUNCTION            1
             14 POP_TOP

  5          16 LOAD_GLOBAL              2 (fun1)
             18 CALL_FUNCTION            0
             20 POP_TOP

  7          22 LOAD_GLOBAL              3 (inspect)
             24 LOAD_ATTR                4 (currentframe)
             26 CALL_FUNCTION            0
             28 STORE_GLOBAL             5 (y)

  8          30 LOAD_CONST               2 (2)
             32 STORE_FAST               3 (f)
             34 LOAD_CONST               0 (None)
             36 RETURN_VALUE

Bytecode fun2 function, each column are:

源码行号 | 指令在函数中的偏移 | 指令符号 | 指令参数 | 实际参数值(参考)

Let's look at the process of implementation of Python methods. Runtime code, bytecode stored in PyCodeObject object. PyCodeObject holds static information compiled at runtime context recombined to form a complete operating mode environment. Function code variable is pointing PyCodeObject objects, you can view the bytecode information.

>>> fun1.__code__.co_code           # 查看字节码
b'd\x01}\x00t\x00j\x01\x83\x00a\x02t\x03t\x02j\x04\x83\x01\x01\x00t\x03d\x02\x83\x01\x01\x00|\x00S\x00'  

>>> list(fun1.__code__.co_code)     # 转换成 list 之后,是由指令符号后面跟着指令参数组成,指令参数根据指令符号不同个数不同
[100, 1, 125, 0, 116, 0, 106, 1, 131, 0, 97, 2, 116, 3, 116, 2, 106, 4, 131, 1, 1, 0, 116, 3, 100, 2, 131, 1, 1, 0, 124, 0, 83, 0]

>>> dis.opname[100]  # dis 模块的 opname 存放了操作码
'LOAD_CONST'         # 100, 1 就是相当于 LOAD_GLOBAL 1

>>> dis.opname[125]  
'STORE_FAST'         # 125, 0 就是相当于 STORE_FAST 0

# PyCodeObject对象中存放这当前上下文的数据
>>> fun1.__code__.co_varnames   # 局部变量名的元组
('b',)

>>> fun1.__code__.co_consts     # 局部变量中的常量元组
(None, 1, 'running fun1')

>>> fun1.__code__.co_names      # 名称的元组
('inspect', 'currentframe', 'x', 'print', 'f_lasti')

>>> fun2.__code__.co_varnames
('d', 'c', 'e', 'f')

>>> fun2.__code__.co_consts
(None, 'running fun2', 2)

>>> fun2.__code__.co_names
('a', 'print', 'fun1', 'inspect', 'currentframe', 'y')

co_code stored bytecode, the bytecode using binary storage, to save storage space, the symbol is a constant corresponding to the instruction, followed by an instruction after the instruction symbol parameters, so easy to operate.

  • Co_varnames tuple contains the local variable names, all the current local variables
  • Co_consts tuple comprising bytecodes literal used, local constants
  • co_names comprising bytecode with the name tuple

You can get information inspect the call stack when the function is executed:

# 运行方法 fun1
# f_lasti 记录当前栈帧中运行的行号
>>> fun1()
16                              
running fun1
1

# 调用栈中存储了字节码信息
>>> x.f_code == fun1.__code__   
True

# co_name 是方法名
>>> x.f_code.co_name
'fun1'

# f_locals 存放局部变量的值
>>> x.f_locals
{'b': 1}

# 上一级调用栈
>>> x.f_back.f_code.co_name  
'<module>'

# 调用方法 fun2
>>> fun2(6)
running fun2
24
running fun1
2

>>> y.f_code.co_name
'fun2'

# 上一级调用栈,fun2 函数调用 fun1 函数,所以 fun1 的上一级调用栈是 fun2
>>> x.f_back.f_code.co_name   
'fun2'

>>> y.f_code.co_names
('a', 'print', 'fun1', 'inspect', 'currentframe', 'y')

>>> y.f_code.co_consts
(None, 'running fun2', 2)

# fun2 方法的局部变量
>>> y.f_locals
{'f': 2, 'e': 6, 'c': 0, 'd': 6}

# fun2 中的全局变量存放在 f_globals 中,并且包含内置变量
>>> y.f_globals['a']
0

It describes several commonly used meaning bytecodes:

LOAD_GLOBAL 0 (a)

LOAD_GLOBAL is taken co_names index tuple value of 0, i.e., a, then find a value of from f_globals, the value of a data stack is pressed into the stack, i.e. the value of 0 onto stack

STORE_FAST 1 (c)

STORE_FAST is taken co_names index tuple value of 1, i.e. c, data stack value is removed of the stack, i.e. just pressed into the top of the stack the value 0, the value stored in the corresponding value of c f_locals, thus completing a the assignment to c, it is { 'c': 0}

LOAD_FAST 0 (d)

LOAD_FAST co_varnames tuple is taken as an index value of 0, i.e., d, d in f_locals lookup value, the value of d data stack 6 is pressed into the stack

STORE_FAST 2 (e)

STORE_FAST index value is taken co_names tuple 2, i.e., e, remove the top of the stack value, stored in the corresponding e f_locals values, i.e., { 'e': 6}

LOAD_GLOBAL 1 (print)

LOAD_CONST 1 ('running fun2')

CALL_FUNCTION 1

POP_TOP

The print and 'running fun2' sequentially onto stack, call_function call functions, data 1 is a top of stack ( 'running fun2') pop as a function call parameters, then the pop-up print, print function call. Execution of the function print ( 'running fun2')

LOAD_FAST 3 (f)

RETURN_VALUE

The value of f is pressed into the top of the stack, the stack is taken out RETURN_VALUE value, as the value returned by the function, is passed on a call stack, the stack starts to run on the call level.

In Python functions and data storage processes are performed separately. When calling function execution based on the call stack, call stack each has its own data marts, data stored in the data stack, call stack interpreter is allocated on the heap memory, so after the end of the function execution stack frame there, The data also retained. CALL_FUNCTION calls while performing other functions, the stack frame uses f_lasti record line number under execution, execution continues at f_lasti when the function returns.

From laboratory building

https://www.shiyanlou.com/courses/1292/learning/

Guess you like

Origin www.cnblogs.com/mrwuzs/p/11986390.html