General expressions in Python virtual machines (i)

Source: Internet
Author: User

In the Python virtual Machine Framework chapter, we see the overall framework of the Python virtual machine through Pyeval_evalframeex. At the beginning of this chapter, we will look at how Python virtual machines are performing the general expressions of Python, where "general expressions" include the most basic object creation statements, and print statements. As for the IF, while and other expressions, we classify them in the control flow statement, which is described in the following sections

Simple creation of built-in objects

Let's start by looking at a simple object creation statement:

demo.py

i = 1s = "Python" D = {}l = []

  

The above statement is very simple, create I, S, D, l four variables, respectively assigned to 1, "Python", Dictionary, list, now, let's look at the script corresponding to the Pycodeobject object of the symbol table co_names and the constant table co_consts contains what

# python2.5......>>> Source = open ("demo.py"). Read () >>> CO = compile (source, "demo.py", "exec") >> > co.co_consts (1, ' Python ', None) >>> co.co_names (' I ', ' s ', ' d ', ' l ')

  

symbol tables and constant tables hold important information about the program's operation and play a very important role in Python virtual machine execution of bytecode directives.

Next, we will use the DIS module to look at the demo.py corresponding byte code

>>> Import dis>>> Dis.dis (CO)  1           0 load_const               0 (1)              3 store_name               0 (i)  2           6 load_const               1 (' Python ')              9 store_name               1 (s)  3          build_map                0             store_name               2 (d)  4          build_list               0             store_name               3 (L)             load_const               2 (None)             27 Return_value  

  

The leftmost column is the number of lines that the bytecode directive corresponds to in the source code, the second column is the current byte code offset in Co_code, the third column shows the current bytecode instruction, the fourth column is the parameter of the instruction, the last column is the actual argument after the calculation

In the implementation of Pyeval_evalframeex, a large number of macros are used for efficiency reasons, some of which include various operations on the stack and access to the tupple element, which are used in large numbers when executing bytecode directives:

Ceval.c

Access Tupple element # define GETITEM (V, i) Pytuple_getitem ((v), (i))//adjust stack top pointer # define BASIC_STACKADJ (n) (stack_pointer + = n) # Define STACKADJ (n) Basic_stackadj (n)//Enter stack operation # define Basic_push (v) (*stack_pointer++ = (v)) #define PUSH (v) basic_push (v)/ /out-of-stack operation # define BASIC_POP () (*--stack_pointer) #define POP () Basic_pop ()

  

Figure 1-1

Figure 1-1 the Stack_pointer on the left is the stack-top pointer of the runtime stack, and the operation of the bytecode instruction on the symbols and constants will eventually be reflected to the runtime stack and the local namespace (f->f_locals)

Our analysis of demo.py combined with dis results in progressive parsing

i = 1//Analysis results 0 load_const 0 (1) 3 Store_name 0 (i)

  

i = 1 produces two bytecode: Load_const and Store_name, we now look at the definition of load_const in the Pyeval_evalframeex function:

Ceval.c

Case load_const:x = GETITEM (consts, Oparg); Py_incref (x); PUSH (x); goto Fast_next_opcode;

  

According to our previous definition, GETITEM (consts, Oparg) is obviously GETITEM (consts, 0), or Pytuple_getitem (consts, 0). The intent of load_const is to read the element with the sequence number 0 from the consts and then execute the push bytecode to press it into the runtime stack Stack_pointer, where consts is f->f_code->co_consts, Where F is the currently active Pyframeobject object, then Consts is the co_consts in the Pycodeobject object.

According to the analysis of the DIS module to demo.py, we can know that the No. 0 element of consts is an integer object 1, which is also the first object created in demo.py. Load_const when the runtime stack and namespace are complete as shown:

Figure 1-2

The first bytecode directive Load_const only changes the runtime stack and has no effect on the local namespace. But don't forget, finish i = 1 besides load_const this instruction, there is also a store_name instruction, Store_name will complete in the local namespace, the implementation of the symbol I to Pyintobject object 1 mapping relationship, so that if we need the symbol I , you can find the corresponding object for I in the local namespace. Now, let's take a look at the implementation of Store_name bytecode

Ceva.lc

Case store_name:w = GETITEM (names, oparg), V = POP (), if ((x = f->f_locals)! = NULL) {if (Pydict_checkexact (x)) Err = Pydi Ct_setitem (x, W, v); elseerr = Pyobject_setitem (x, W, v); Py_decref (v); if (err = = 0) continue;break;} Pyerr_format (Pyexc_systemerror, "No locals found when storing%s", Pyobject_repr (w));

  

Here, we only consider f->f_locals is the case of the Pydictobject object, the code will first take the symbol table symbol, and remove the symbol from the runtime stack corresponding to the value, in the Pydictobject this object to establish a mapping relationship, and according to the above dis on i = 1 of the analysis, you can find that the store_name out of the symbol is indeed I. When the Store_name command is completed, the runtime stack and the local namespace division become as follows:

Figure 1-3

  

The demo.py of s = "Python" corresponds to the same byte code as I = 1, which is no longer explained here.

Now, let's take a look at the third line of demo.py. D = {}, how to create a Dictionary object

D = {}//analysis results 3 build_map  0  store_name 2 (d)

  

The instruction Build_map creates a Pydictobject object and presses it into the stack

Ceval.c

Case build_map:x = Pydict_new (); PUSH (x); if (x! = NULL) Continue;break;

  

Some people might think that if you declare a dictionary not just an empty dictionary, but instead fill in the parameters? such as: D = {"1": 1, "2": 2}, do not worry about how the corresponding bytecode of such a dictionary is generated and executed, will be introduced later, after the execution of Build_map and store_name, we then look at the runtime stack and namespaces:

Figure 1-4

Take a look at the demo.py last line of code L = [], let's look at the results of its analysis:

L = []//analysis results 4 build_list    0  store_name    3 (L)  load_const    2 (None)  Return_value  

  

Build_list This bytecode is slightly better than build_map because it does not create an empty dictionary like Build_map and presses it directly into the stack, but generates a list based on the elements in the list

Ceval.c

Case build_list:x =  pylist_new (oparg), if (x! = NULL) {for (;--oparg >= 0;) {w = POP (); Pylist_set_item (x, Oparg, w);} PUSH (x); continue;} Break

  

Here we can make a guess, if build_list created not an empty list, then there must be a number of load_const operations, which will cause a number of elements to press into the runtime stack, when executing build_list, these elements will be ejected from the runtime stack, Join the newly created Pylistobject object. Finally, the store_name is executed to complete the mapping of the symbol to the list in the stack. The distribution of runtime stacks and namespaces should now be as follows:

Figure 1-5

Here, it seems demo.py all the code has been executed, but it seems that we have missed something? After creating the list and establishing the mappings, there are two more byte codes:

Load_const    2 (None) Return_value  

  

Now that the objects have been created, what is the use of the extra two sentences? It turns out that Python must return some values after executing a code block, which is used to return these values, load_const the None object into the runtime stack, and then return_value the objects in the stack. Which means none returns.

Ceval.c

Case return_value:retval = POP (), why = Why_return;goto fast_block_end;

  

General expressions in Python virtual machines (i)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.