Transfer of positional parameters
Non-parametric invocation of Python virtual machine function mechanism (i)
Python virtual machine function mechanism name space (ii)
Parameters category of Python virtual machine function mechanism (iii)
Before we have analyzed the invocation of the parameterless function, let's look at how Python implements the call with the parameter function. In fact, the basic invocation process is the same as the parameterless function, but the difference is that the Python virtual machine must pass parameters when the parameter function is called. Let's look at a piece of code first:
# cat Demo2.pydef F (name, age): Age + 5 print ("[", Name, Age, "]") Age = 5f ("Robert", age)
We use the DIS module to compile the corresponding byte code:
# python2.5......>>> Source = open ("demo2.py"). Read () >>> CO = compile (source, "demo2.py", "exec") >> > Import dis>>> Dis.dis (CO) 1 0 load_const 0 (<code object f at 0x7ff044b1c648, file "demo2.py ", line 1>) 3 make_function 0 6 store_name 0 (f) 6 9 load_const 1 (5) store_ NAME 1 (age) 7 load_name 0 (f) load_const 2 (' Robert ') load_name 1 ( Age) call_function 2 pop_top load_const 3 (None) to Return_value
Then use the DIS module to compile the corresponding byte code for the function f:
>>> from Demo2 import f>>> Dis.dis (f) 2 0 load_fast 1 (age) 3 load_const 1 (5) 6 Inplace_add 7 store_fast 1 (age) 3 load_const 2 (' [') load_fast 0 (name) C13/>16 load_fast 1 (age) load_const 3 ('] ')- build_tuple 4 print_item 26 Print_newline load_const 0 (None) Return_value
In the compiled demo2.py, there are three load instructions before the call_function instruction, as shown in the runtime stack 1-1 after the completion of the three load instruction:
Figure 1-1 Run-time stack before call_function instruction executes
As you can see, the parameters required by the function are already pressed into the runtime stack. Next, execute the call_function instruction with an instruction parameter of 2
Ceval.c
Static Pyobject * Call_function (pyobject ***pp_stack, int oparg) {int na = oparg & 0xff;int nk = (oparg >> 8) &am P 0xff;int n = Na + 2 * NK; Pyobject **pfunc = (*pp_stack)-n-1; Pyobject *func = *pfunc, ...}
As we mentioned earlier, the call_function directive parameter Oparg, the low byte contains the number of positional parameters, the so-called positional parameter, which is the general function as seen in F. The high byte in Oparg contains the number of another parameter. So na=2,nk=2, so n=2. Starting at the top of the stack pointer, Pp_stack 2, the Pyobject *func correctly points to the Pyfunctionobject object that is stored in the runtime stack that represents F. Then, the program enters Fast_function
Ceval.c
Static Pyobject * Fast_function (pyobject *func, pyobject ***pp_stack, int n, int na, int nk) {Pycodeobject *co = (pycodeobj ECT *) Pyfunction_get_code (func); Pyobject *globals = Pyfunction_get_globals (func); Pyobject *argdefs = Pyfunction_get_defaults (func); Pyobject **d = null;int nd = 0;......if (argdefs = NULL && Co->co_argcount = = N && nk = = 0 &&CO-&G T;co_flags = = (Co_optimized | Co_newlocals | Co_nofree)) {Pyframeobject *f; Pyobject *retval = NULL; Pythreadstate *tstate = Pythreadstate_get (); Pyobject **fastlocals, **stack;int i; Pcall (pcall_faster_function); assert (Globals! = NULL);//[1]: Create Pyframeobject object corresponding to function f = pyframe_new (Tstate, CO, Globals, NULL);//[2]: copy function parameter: From run-time stack to pyframeobject.f_localsplusfastlocals = F->f_localsplus;stack = (*pp_stack)-N; for (i = 0; i < n; i++) {py_incref (*stack); fastlocals[i] = *stack++;} ......} if (argdefs! = NULL) {d = &pytuple_get_item (argdefs, 0); nd = ((Pytupleobject *) argdefs)->ob_size;} Return Pyeval_evalcodeex (Co, globals, (PyOBject *) NULL, (*pp_stack)-N, Na, (*pp_stack)-2 * nk, NK, D, ND, Pyfunction_get_closure (func));}
The Pyframeobject object corresponding to function f is created at [1] of the code above, and in this process, the Pycodeobject object saved in the corresponding Pyfunctionobject object of function f is passed to the newly created Pyframeobject object. Then, at code [2], the Python virtual machine copies the parameters individually to the f_localsplus of the newly created Pyframeobject object. The memory block that F_localsplus points to contains the run-time stack used by the Python virtual machine, so what is the memory-space relationship that the parameters occupy in the memory space of the runtime stack? The answer is in pyframe_new.
Frameobject.c
Pyframeobject *pyframe_new (pythreadstate *tstate, Pycodeobject *code, Pyobject *globals, PyObject *locals) {PyFrameObje CT *back = tstate->frame; Pyframeobject *f; py_ssize_t i;......if (code->co_zombieframe! = NULL) {...} else {py_ssize_t extras, ncells, nfrees; Ncells = Pytuple_get_size (code->co_cellvars); Nfrees = Pytuple_get_size (code->co_freevars); Extras = code->co_stacksize + code->co_nlocals + ncells + nfrees; if (free_list = = NULL) {//[1]: request Extras memory space for f_localsplus f = Pyobject_gc_newvar (Pyframeobject, &PYF Rame_type, extras); if (f = = NULL) {py_decref (builtins); return NULL; }} ... F->f_code = code;//[2]: Gets the number of memory left outside the runtime stack in f_localsplus extras = Code->co_nlocals + Ncells + Nfrees;f->f_vAluestack = F->f_localsplus + extras;for (i=0; i<extras; i++) f->f_localsplus[i] = NULL; F->f_stacktop = F->f_valuestack;......return F;}
In the Co_nlocals field of the Pycodeobject object corresponding to the function, the number of arguments to the function is included, because the function parameter is also one of the local symbols. In the above code [2], starting from F_localsplus, the calculated extras of the memory, there must be for the function parameters used memory. In other words, the function parameters are stored in the memory before the runtime stack.
As you can see from the process of creating a Pyframeobject object from Pyframe_new, in F_localsplus, the space that is used to store the function arguments and the runtime stack is logically separated, not the same piece of memory, although the two contiguous memory points to the same object, But their boundaries are clear, squarely
After processing the parameters, there is no entry into the Pyeval_evalframeex, so the runtime stack is still empty. However, the function is already in f_localsplus. At this point, the newly created Pyframeobject object is shown in F_localsplus domain 1-1:
Figure 1-1 the memory layout of the new Pyframeobject object before entering Pyeval_evalframeex
Access to positional parameters
When the action of the parameter copy is completed, it will enter the new Pyeval_evalframeex and start the actual function F's call work.
def f (Name, age): Age + = 5//bytecode instruction 0 load_fast 1 (age) 3 Load_const 1 (5) 6 Inplace_add 7 store_fast 1 (A GE) print ("[", Name, Age, "]") ...
Age + = 5 compiled instruction sequence, there are two are we have never dissected, one is load_fast, the other is store_fast, and it is these two instructions, complete function parameters read and write
Ceval.c
Pyobject * Pyeval_evalframeex (pyframeobject *f, int throwflag) {register pyobject **fastlocals; #define Getlocal (i) ( Fastlocals[i]) Fastlocals = F->f_localsplus, ...} #define GETLOCAL (i) (Fastlocals[i]) Case load_fast:x = getlocal (Oparg); if (x! = NULL) {py_incref (x); PUSH (x); goto Fast_next_opcode;} Format_exc_check_arg (Pyexc_unboundlocalerror, Unboundlocal_error_msg, Pytuple_getitem (Co->co_varnames, Oparg)) #define SETLOCAL (I, value) do { Pyobject *tmp = getlocal (i); Getlocal (i) = value; PY_XDECREF (TMP); } while (0) Case store_fast:v = POP (); SETLOCAL (Oparg, v); goto Fast_next_opcode; Predicted (pop_top);
Originally, Load_fast and store_fast This pair of instruction is to f_localsplus this piece of memory as the operation target. The result of the instruction "0 Load_fast 1" is to press the object in f_localsplus[1] into the runtime stack, and from figure 1-1 we can see that f_localsplus[1] is the age. After completing the addition operation, the result is put into f_localsplus[1] by store_fast, so the update of the variable age is achieved, and the result is 10 when the print accesses the time parameter.
As for the positional parameters of a function in Python, we understand how it is passed in the function call process, and how the function execution process is accessed and modified. When the function is called, Python presses the function arguments from left to right into the runtime stack, and in fast_function, copies the parameters to the newly created f_localsplus of the Pyframeobject object corresponding to the function. The end result is that the Python virtual machine stores the arguments passed in when the function is called, from left to right in the f_localsplus of the newly created Pyframeobject object
When accessing a function parameter, the Python virtual machine does not follow the usual access notation to access the namespace, but instead accesses the object that corresponds to the symbol stored in the F_localsplus by an index (offset location). This method of accessing by index (offset location) is also the origin of the "positional parameter" name
Location parameters of Python virtual machine function mechanism (iv)