General expressions in Python virtual machines (iii)

Source: Internet
Author: User
Tags builtin strcmp throw exception

Other general expressions

In the first two chapters: a generic expression in a Python virtual machine (a), a generic expression in a Python virtual machine (ii), we describe how the Python virtual machine performs the creation of an integer value object, a String object, a Dictionary object, and a list object. Now, let's learn about variable assignments, variable operations, and print operations, how Python executes

As before, let's take a look at the symbol table and constants corresponding to the Pycodeobject of the normal.py.

# cat normal.py a = 5b = AC = a + bprint (c) # python2.5......>>> Source = open ("normal.py"). Read () >>> CO = Co Mpile (source, "normal.py", "exec") >>> co.co_names (' A ', ' B ', ' C ') >>> co.co_consts (5, None)

  

and the byte code instructions for normal.py.

>>> Import dis>>> Dis.dis (CO)  1           0 load_const               0 (5)              3 store_name               0 (a)  2           6 load_name                0 (a)              9 store_name               1 (b)  3          load_name                0 (a)             load_name                1 (b)             binary_add                       store_name               2 (c)  4          load_name                2 (c)             Print_item                       26 Print_newline                    load_const               1 (None)             Return_value        

  

The byte code instruction for the first line A = 5 is no longer described here, we begin to look at the second line of B = a

b = a//Analysis Results 2  6 load_name    0 (a) 9 store_name   1 (b)

  

Above we see a new directive: Load_name, here, let's look at the contents of the Load_name directive, see what it does, how it works with Store_name, the completion of the assignment statement

Ceval.c

Case load_name:w = GETITEM (names, Oparg), if ((v = f->f_locals) = = NULL) {Pyerr_format (Pyexc_systemerror,     "no local s when loading%s ",     Pyobject_repr (w)); if (Pydict_checkexact (v)) {//[1]: finds the variable name in the local namespace corresponding to the value x = Pydict_getitem (V, W); Py_xincref (x);} else {x = Pyobject_getitem (V, W); if (x = = NULL && pyerr_occurred ()) {if (! Pyerr_exceptionmatches (Pyexc_keyerror)) break; Pyerr_clear ();}} if (x = = null) {//[2]: Look for variable names in the global namespace x = Pydict_getitem (F->f_globals, W), if (x = = null) {//[3]: Find in Builtin namespace Variable name corresponding value x = Pydict_getitem (F->f_builtins, W), if (x = = NULL) {//[4]: Lookup variable name failed, throw exception Format_exc_check_arg (    pyexc_ Nameerror,    name_error_msg, w); break;}} Py_incref (x);} PUSH (x); continue;

  

Load_name actually seems a lot of content, actually very simple, combined with the above code comments [1], [2], [3], we can know, load_name is nothing more than in the local, global and builtin three namespaces to find a variable name corresponding value, If the Builtin namespace is not found, an exception is thrown. The value corresponding to the variable name is found and then pressed into the runtime stack. Finally, the assignment statement is completed with Store_name.

The official Python document also describes the search for variables that follow the local scope (local namespace), the global scope (globally namespace), and the built-in scope (the Builtin namespace) until the search succeeds or all 3 scopes are searched

Let's change the Load_name code a little bit, and then recompile and install Python

Case load_name:w = GETITEM (names, Oparg), if ((v = f->f_locals) = = NULL) {Pyerr_format (Pyexc_systemerror, "no locals whe N Loading%s ", Pyobject_repr (w)); if (Pydict_checkexact (v)) {x = Pydict_getitem (V, W);//[1]if (strcmp (pystring_asstring (w), "PYTHONVM") ==0) {printf ("[ LOAD name]:search pyobject%s in local NAME space...%s\n ", Pystring_asstring (w), X==null? "False": "Success");} Py_xincref (x);} else {x = Pyobject_getitem (V, W); if (x = = NULL && pyerr_occurred ()) {if (! Pyerr_exceptionmatches (Pyexc_keyerror)) break; Pyerr_clear ();}} if (x = = NULL) {x = Pydict_getitem (f->f_globals, W);//[2]if (strcmp (pystring_asstring (w), "PYTHONVM") ==0) {printf ("[ LOAD name]:search pyobject%s in global NAME space...%s\n ", Pystring_asstring (w), X==null? "False": "Success");} if (x = = NULL) {x = Pydict_getitem (F->f_builtins, W);//[3]if (strcmp (pystring_asstring (w), "PYTHONVM") ==0) {printf ("[ LOAD name]:search pyobject%s in builtin NAME space...%s\n ", Pystring_asstring (w), X==null? "False": "Success");}if (x = = NULL) {//[4]if (strcmp (pystring_asstring (w), "PYTHONVM") ==0) {printf ("[LOAD name]:search pyobject%s faild\n", Pystring_asstring (w));} Format_exc_check_arg (Pyexc_nameerror,name_error_msg, w); break;}} Py_incref (x);} PUSH (x); continue;

  

We add code at [1], [2], [3], [4], and try to search for the symbol process

# python2.5......>>> Print pythonvm[load name]:search pyobject pythonvm in local NAME space ... False[load name]:search pyobject PYTHONVM in global NAME space ... False[load name]:search pyobject PYTHONVM in builtin NAME space ... False[load name]:search pyobject PYTHONVM faildtraceback (most recent call last):  File ' <stdin> ', line 1, in < ; Module>nameerror:name ' PYTHONVM ' is not defined

  

We have not defined PYTHONVM this variable, in the printing PYTHONVM this symbol will first get the PYTHONVM corresponding value, that is, will execute load_name this instruction, you can see that the search variable is indeed by local, global, Builtin these three namespaces search

Numeric operations

We have described how to execute the byte code of the assignment operation in the previous section, and we understand the numerical operation based on the previous content.

c = A + b//analysis results 312 load_name                0 (a) load_name                1 (b) Binary_add               2 (c)

  

Load_name will read the values of A and B, and press into the runtime stack, and then add operations through Binary_add, according to the following store_name, can guess in the binary_add, the results of the operation has been pushed into the runtime stack, and finally store_ Name pops up its result, establishing a relationship between the symbol and the value. Now, let's take a look at Binary_add.

Case binary_add:w = POP (), V = TOP (), if (Pyint_checkexact (v) && pyint_checkexact (W)) {//[1] : Fast channel Register with Pyintobject object added, long A, B, i;a = Pyint_as_long (v), B = Pyint_as_long (w), i = A + b;//[2]: If the result overflows, turn to the slow channel if ( I^a) < 0 && (I^B) < 0) {goto slow_add;} x = Pyint_fromlong (i);} [3]:pystringobject object Add fast channel else if (Pystring_checkexact (v) && pystring_checkexact (W)) {x = String_ Concatenate (V, W, F, next_instr);/* String_concatenate consumed the ref to V */goto SKIP_DECREF_VX;} else {  //[4]: Slow channel for general object addition  slow_add:x = Pynumber_add (V, W);} Py_decref (v);  Skip_decref_vx:py_decref (w); Set_top (x); if (x! = NULL) Continue;break;

  

From the above code [1], [2], [3], [4] can be seen, if the object is an int object, then the value is taken out, and then add and then detect whether overflow, if overflow then walk the object add the slow channel, if no overflow is returned, if the Pystringobject object is added, Creates a new Pystringobject object based on the sum of the results returned

If the object participating in the operation is a condition other than these two fast channels, then only the slow channel Pynumber_add completes the addition operation. In Pynumber_add, Python virtual opportunity makes a lot of type judgments, looking for additional work, such as an addition operation function corresponding to an object, that is much slower than the first two acceleration mechanisms. In general, the Python virtual machine first checks the type object of the object participating in the Pynumber_add, checks if the Nb_add in the Pynumbermethods can complete the addition operation on V and W, if not, It also checks whether Sq_concat in Pysequencemethods can be completed, and if not, throws an exception

One thing to note here is that although the Python virtual machine has a fast channel for the Pyintobject object, if the calculation results overflow, the Python virtual opportunity discards the fast-track calculation and turns to the slow channel. To verify what we said earlier, we re-modified the Binary_add code to include the monitoring code at [1], [2], [3]:

Case binary_add:w = POP (), V = TOP (), if (Pyint_checkexact (v) && pyint_checkexact (W)) {/* inline:int + int */registe R long A, B, i;a = Pyint_as_long (v), B = Pyint_as_long (w), i = A + b;if ((i ^ a) < 0 && (i ^ b) < 0) {//[1]prin TF ("[Binary_add]:%ld +%ld in quick channel...overflow\n", A, b); goto Slow_add;} [2]printf ("[Binary_add]:%ld +%ld in quick channel...success\n", A, b); x = Pyint_fromlong (i);} else if (pystring_checkexact (v) && pystring_checkexact (W)) {x = String_concatenate (V, W, F, next_instr);/* string _concatenate consumed the ref to V */goto SKIP_DECREF_VX;} Else{slow_add://[3]if (Pyint_checkexact (v) && pyint_checkexact (W)) {Register long A, B;a = Pyint_as_long (v); b = Pyint_as_long (W);p rintf ("[Binary_add]:%ld +%ld switch to slow channel\n", A, b);} x = Pynumber_add (V, W);} Py_decref (v); Skip_decref_vx:py_decref (w); Set_top (x); if (x! = NULL) Continue;break;

  

Then compile and install Python and test the behavior of Binary_add:

# python2.5......>>> A = 1>>> B = 2>>> A + b[binary_add]:1 + 2 in quick channel...success3>>& Gt c = 9223372036854775807>>> D = c + c[binary_add]:9223372036854775807 + 9223372036854775807 in quick channel...ove rflow[binary_add]:9223372036854775807 + 9223372036854775807 Switch to slow channel>>> type (d) <type ' long ' >

  

Information output

Finally, to see the print action, in the previous normal.py, we finally printed the C object, we look at the corresponding byte code:

Print (c)//Analysis Results 422 Load_name                2 (c) Print_item-          Print_newline       

  

Before you print the object, be sure to get its value, so the first bytecode instruction is Load_name, takes the value of C out of the namespace, presses it into the runtime stack, and finally finishes the print operation with Print_item

Case print_item:v = POP (); if (stream = = NULL | | stream = = py_none) {w = Pysys_getobject ("stdout"); if (w = = null) {Pyerr_sets Tring (Pyexc_runtimeerror, "lost Sys.stdout"); err =-1;}} Py_xincref (w); if (w! = NULL && pyfile_softspace (w, 0)) Err = Pyfile_writestring ("", w); if (err = = 0) Err = Pyfile_w Riteobject (V, W, Py_print_raw);..//Omit part of the cost Py_xdecref (W); Py_decref (v); Py_xdecref (stream); stream = null;if (err = = 0) continue;break;

  

Python determines whether an object named stream is null when printing, and if NULL, sets the W to the standard output stream. So, what is stream? It is actually a Pyobject object defined in the Pyeval_evalframeex

Register Pyobject *stream = NULL;

  

If the output is by the following Python code:

# cat demo3.py f = open ("Test", "W") print >> F, # python2.5......>>> Source = open ("demo3.py"). Read () >>& Gt CO = compile (source, "demo3.py", "exec") >>> import dis>>> Dis.dis (CO)  1           0 load_name                0 ( Open)              3 load_const               0 (' test ')              6 load_const               1 (' W ')              9 call_function            2             store_name               1 (f)  2          load_name                1 (f)             dup_top                          load_const               2 (1) from             rot_two                          Print_ Item_to                    print_newline_to                 load_const               3 (None)             Return_value        

  

Then the print_newline_to command will be executed before executing the Print_item.

Case print_item_to:w = stream = POP ();/* fall through to Print_item */case Print_item:   ...

  

As you can see, the stream is assigned a value when the print_newline_to is executed, and it is also assigned to W. So the stream is actually used as a judging condition, and the actual output target is W. The reason to use this stream multiple times is that the variable w may also be used in other bytecode directives, so it is not possible to determine if w is null for output to the standard output stream, as you can see, at the end of the Print_item, the stream is set to NULL, Prepare for the next output judgment

After obtaining the output target and the object to be output, Print_item will eventually call through the call sequence of Pyfile_writeobject->pyobject_print->internal_print v->ob_type- The >tp_print waits for output from the output object itself to carry the output function. If the object does not have a tp_print defined, it calls Tp_str or Tp_repr to get the string representation of the object and then outputs the string

General expressions in Python virtual machines (iii)

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.