Running Principle of Python Essay Virtual Machine

Keywords: Python REST encoding

When it comes to Python's operating mechanism, you have to start with. pyc files and bytecodes. The PyCodeObject object is created when the module is loaded, that is, import.

.pyc file

  1. Executing python test.py compiles test.py into bytecode and interprets execution, but does not generate test.pyc
  2. If other modules such as import urllib2 are loaded in test.py, python compiles urllib2.py into bytecode, generates urllib2.pyc, and then interprets and executes the bytecode.
  3. If you want to generate test. pyc, you can compile it using Python built-in module py_compile. You can also execute the command python-m py_compile test.py, which generates test.pyc
  4. When loading a module, python runs with. pyc if both. py and. pyc exist, and if. pyc compiles earlier than. py, it recompiles the. py file and updates the. pyc file.

PyCodeObject

The compilation process of Python code is to compile PyCodeObject objects. Here is the PyCodeObject definition for Python 3.5.7

/* Bytecode object */
typedef struct {
    PyObject_HEAD
    int co_argcount;		/* #arguments, except *args CodeBlock Number of Median Location Parameters */
    int co_kwonlyargcount;	/* #keyword only arguments */
    int co_nlocals;		/* #local variables */
    int co_stacksize;		/* #entries needed for evaluation stack */
    int co_flags;		/* CO_..., see below */
    PyObject *co_code;		/* instruction opcodes */
    PyObject *co_consts;	/* list (constants used) */
    PyObject *co_names;		/* list of strings (names used) */
    PyObject *co_varnames;	/* tuple of strings (local variable names) */
    PyObject *co_freevars;	/* tuple of strings (free variable names) */
    PyObject *co_cellvars;      /* tuple of strings (cell variable names) */
    /* The rest aren't used in either hash or comparisons, except for
       co_name (used in both) and co_firstlineno (used only in
       comparisons).  This is done to preserve the name and line number
       for tracebacks and debuggers; otherwise, constant de-duplication
       would collapse identical functions/lambdas defined on different lines.
    */
    unsigned char *co_cell2arg; /* Maps cell vars which are arguments. */
    PyObject *co_filename;	/* unicode (where it was loaded from) */
    PyObject *co_name;		/* unicode (name, for reference) */
    int co_firstlineno;		/* first source line number */
    PyObject *co_lnotab;	/* string (encoding addr<->lineno mapping) See
				   Objects/lnotab_notes.txt for details. */
    void *co_zombieframe;     /* for optimization only (see frameobject.c) */
    PyObject *co_weakreflist;   /* to support weakrefs to code objects */
} PyCodeObject;
  1. co_argcount,co_kwonlyargcount

PEP 3102: http://www.python.org/dev/peps/pep-3102/

Keyword-only argument: In the list of function parameters, named parameters that appear after * varargs can only be invoked in the form of key parameters.

Function calls are the order in which parameters are assigned: position parameters - > keyword parameters - > variable parameters (* varargs)

co_argcount: Number of positional parameters in CodeBlock, that is, the number of positional parameters that occur when invoked (excluding variable parameters * varargs).

co_kwonlyargcount: The number of key parameters in CodeBlock, that is, the number of parameters that appear after the variable parameters (* varargs) at the time of invocation. The parameters after the variable parameters are the key parameters in the form of "keyvalue".

>>> def func(a, b, *d, c):
...     m = 1
...     pass
...
>>> func.__code__.co_argcount
>>> func.__code__.co_kwonlyargcount
  1. co_nlocals: Number of all local variables in Code Block, including the parameters of code block (co_argcount+co_kwonlyargcount+number of variable parameters)+local variables in code block
>>> f1.__code__.co_nlocals
7

a, b, c, m, 4

  1. co_stacksize: Number of stack spaces required to execute this Code Block
>>> f1.__code__.co_stacksize
1
  1. Bytecode instruction sequence compiled by co_code: Code Block
>>> f1.__code__.co_code
b'd\x01\x00}\x06\x00d\x00\x00S'
  1. co_consts,co_names
  • co_consts: tuples of all constants in Code Block
  • co_names: tuples of all symbols (names) in Code Block
>>> f1.__code__.co_consts
(None, 1)
>>> f1.__code__.co_names
()
  1. co_filename,co_name
  • co_filename: The complete path of the. py file corresponding to Code Block
  • co_name: The name of Code Block, usually the name of a function or class.
>>> f1.__code__.co_filename
'<stdin>'#Because it's in the console, it's stdin.
>>> f1.__code__.co_name
'f1'
  1. co_firstlineno: The starting line of Code Block in the corresponding. py file test_1.py
def func(a, b, c, *d, e, f):
    m = 1
    pass
print(f1.__code__.co_firstlineno)

output

1
  1. co_varnames,co_freevars,co_cellvars
  • co_varnames: Variables assigned in this code segment but not referenced by the inner code segment
  • co_freevars (freevars: free variables): Variables referenced in this code segment and assigned in the outer code segment
  • co_cellvars (cellvars: Variables constrained by the underlying code): Variables assigned in this code segment and referenced by the underlying code segment

Normal Function Code Segment Testing

def func(a, b, c, *d, e, f):
    m = 1
    pass

print('co_argcount        :', func.__code__.co_argcount)
print('co_kwonlyargcount  :', func.__code__.co_kwonlyargcount)
print('co_nlocals         :', func.__code__.co_nlocals)
print('co_stacksize       :', func.__code__.co_stacksize)
print('co_flags           :', func.__code__.co_flags)
print('co_code            :', func.__code__.co_code)
print('co_consts          :', func.__code__.co_consts)
print('co_names           :', func.__code__.co_names)
print('co_varnames        :', func.__code__.co_varnames)
print('co_freevars        :', func.__code__.co_freevars)
print('co_cellvars        :', func.__code__.co_cellvars)
print('co_filename        :', func.__code__.co_filename)
print('co_name            :', func.__code__.co_name)
print('co_firstlineno     :', func.__code__.co_firstlineno)
print('co_lnotab          :', func.__code__.co_lnotab)

output

co_argcount        : 3
co_kwonlyargcount  : 2
co_nlocals         : 7
co_stacksize       : 1
co_flags           : 71
co_code            : b'd\x01\x00}\x06\x00d\x00\x00S'
co_consts          : (None, 1)
co_names           : ()
co_varnames        : ('a', 'b', 'c', 'e', 'f', 'd', 'm')
co_freevars        : ()
co_cellvars        : ()
co_filename        : pyvm_test2_function.py
co_name            : func
co_firstlineno     : 1
co_lnotab          : b'\x00\x01\x06\x01'

Nested function code testing:

def func(a, b, c, *d, e, f):
    m = 1
    def wapper():
        n = m
    print('wapper-->co_argcount        :', wapper.__code__.co_argcount)
    print('wapper-->co_kwonlyargcount  :', wapper.__code__.co_kwonlyargcount)
    print('wapper-->co_nlocals         :', wapper.__code__.co_nlocals)
    print('wapper-->co_stacksize       :', wapper.__code__.co_stacksize)
    print('wapper-->co_flags           :', wapper.__code__.co_flags)
    print('wapper-->co_code            :', wapper.__code__.co_code)
    print('wapper-->co_consts          :', wapper.__code__.co_consts)
    print('wapper-->co_names           :', wapper.__code__.co_names)
    print('wapper-->co_varnames        :', wapper.__code__.co_varnames)
    print('wapper-->co_freevars        :', wapper.__code__.co_freevars)
    print('wapper-->co_cellvars        :', wapper.__code__.co_cellvars)
    print('wapper-->co_filename        :', wapper.__code__.co_filename)
    print('wapper-->co_name            :', wapper.__code__.co_name)
    print('wapper-->co_firstlineno     :', wapper.__code__.co_firstlineno)
    print('wapper-->co_lnotab          :', wapper.__code__.co_lnotab)

print('func-->co_argcount        :', func.__code__.co_argcount)
print('func-->co_kwonlyargcount  :', func.__code__.co_kwonlyargcount)
print('func-->co_nlocals         :', func.__code__.co_nlocals)
print('func-->co_stacksize       :', func.__code__.co_stacksize)
print('func-->co_flags           :', func.__code__.co_flags)
print('func-->co_code            :', func.__code__.co_code)
print('func-->co_consts          :', func.__code__.co_consts)
print('func-->co_names           :', func.__code__.co_names)
print('func-->co_varnames        :', func.__code__.co_varnames)
print('func-->co_freevars        :', func.__code__.co_freevars)
print('func-->co_cellvars        :', func.__code__.co_cellvars)
print('func-->co_filename        :', func.__code__.co_filename)
print('func-->co_name            :', func.__code__.co_name)
print('func-->co_firstlineno     :', func.__code__.co_firstlineno)
print('func-->co_lnotab          :', func.__code__.co_lnotab)
print('=========================================================')
func(1, 2, 3, 4, 5, 6, 7, e = 8, f = 9)

output

func-->co_argcount        : 3
func-->co_kwonlyargcount  : 2
func-->co_nlocals         : 7
func-->co_stacksize       : 3
func-->co_flags           : 7
func-->co_code            : b'd\x01\x00\x89\x00\x00\x87\x00\x00f\x01\x00d\x02\x00d\x03\x00\x86\x00\x00}\x06\x00t\x00\x00d\x04\x00|\x06\x00j\x01\x00j\x02\x00\x83\x02\x00\x01t\x00\x00d\x05\x00|\x06\x00j\x01\x00j\x03\x00\x83\x02\x00\x01t\x00\x00d\x06\x00|\x06\x00j\x01\x00j\x04\x00\x83\x02\x00\x01t\x00\x00d\x07\x00|\x06\x00j\x01\x00j\x05\x00\x83\x02\x00\x01t\x00\x00d\x08\x00|\x06\x00j\x01\x00j\x06\x00\x83\x02\x00\x01t\x00\x00d\t\x00|\x06\x00j\x01\x00j\x07\x00\x83\x02\x00\x01t\x00\x00d\n\x00|\x06\x00j\x01\x00j\x08\x00\x83\x02\x00\x01t\x00\x00d\x0b\x00|\x06\x00j\x01\x00j\t\x00\x83\x02\x00\x01t\x00\x00d\x0c\x00|\x06\x00j\x01\x00j\n\x00\x83\x02\x00\x01t\x00\x00d\r\x00|\x06\x00j\x01\x00j\x0b\x00\x83\x02\x00\x01t\x00\x00d\x0e\x00|\x06\x00j\x01\x00j\x0c\x00\x83\x02\x00\x01t\x00\x00d\x0f\x00|\x06\x00j\x01\x00j\r\x00\x83\x02\x00\x01t\x00\x00d\x10\x00|\x06\x00j\x01\x00j\x0e\x00\x83\x02\x00\x01t\x00\x00d\x11\x00|\x06\x00j\x01\x00j\x0f\x00\x83\x02\x00\x01t\x00\x00d\x12\x00|\x06\x00j\x01\x00j\x10\x00\x83\x02\x00\x01d\x00\x00S'
func-->co_consts          : (None, 1, <code object wapper at 0x000002A033189B70, file "pyvm_test3_function.py", line 3>, 'func.<locals>.wapper', 'wapper-->co_argcount        :', 'wapper-->co_kwonlyargcount  :', 'wapper-->co_nlocals         :', 'wapper-->co_stacksize       :', 'wapper-->co_flags           :', 'wapper-->co_code            :', 'wapper-->co_consts          :', 'wapper-->co_names           :', 'wapper-->co_varnames        :', 'wapper-->co_freevars        :', 'wapper-->co_cellvars        :', 'wapper-->co_filename        :', 'wapper-->co_name            :', 'wapper-->co_firstlineno     :', 'wapper-->co_lnotab          :')
func-->co_names           : ('print', '__code__', 'co_argcount', 'co_kwonlyargcount', 'co_nlocals', 'co_stacksize', 'co_flags', 'co_code', 'co_consts', 'co_names', 'co_varnames', 'co_freevars', 'co_cellvars', 'co_filename', 'co_name', 'co_firstlineno', 'co_lnotab')
func-->co_varnames        : ('a', 'b', 'c', 'e', 'f', 'd', 'wapper')
func-->co_freevars        : ()
func-->co_cellvars        : ('m',)
func-->co_filename        : pyvm_test3_function.py
func-->co_name            : func
func-->co_firstlineno     : 1
func-->co_lnotab          : b'\x00\x01\x06\x01\x12\x02\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01\x13\x01'
=========================================================
wapper-->co_argcount        : 0
wapper-->co_kwonlyargcount  : 0
wapper-->co_nlocals         : 1
wapper-->co_stacksize       : 1
wapper-->co_flags           : 19
wapper-->co_code            : b'\x88\x00\x00}\x00\x00d\x00\x00S'
wapper-->co_consts          : (None,)
wapper-->co_names           : ()
wapper-->co_varnames        : ('n',)
wapper-->co_freevars        : ('m',)
wapper-->co_cellvars        : ()
wapper-->co_filename        : pyvm_test3_function.py
wapper-->co_name            : wapper
wapper-->co_firstlineno     : 3
wapper-->co_lnotab          : b'\x00\x01'

Closure function test:
Output:

func-->co_argcount        : 3
func-->co_kwonlyargcount  : 2
func-->co_nlocals         : 7
func-->co_stacksize       : 3
func-->co_flags           : 7
func-->co_code            : b'd\x01\x00\x89\x00\x00\x87\x00\x00f\x01\x00d\x02\x00d\x03\x00\x86\x00\x00}\x06\x00|\x06\x00S'
func-->co_consts          : (None, 1, <code object wapper at 0x0000019920289B70, file "pyvm_test4_function.py", line 3>, 'func.<locals>.wapper')
func-->co_names           : ()
func-->co_varnames        : ('a', 'b', 'c', 'e', 'f', 'd', 'wapper')
func-->co_freevars        : ()
func-->co_cellvars        : ('m',)
func-->co_filename        : pyvm_test4_function.py
func-->co_name            : func
func-->co_firstlineno     : 1
func-->co_lnotab          : b'\x00\x01\x06\x01\x12\x02'
=========================================================
f3-->co_argcount        : 0
f3-->co_kwonlyargcount  : 0
f3-->co_nlocals         : 1
f3-->co_stacksize       : 1
f3-->co_flags           : 19
f3-->co_code            : b'\x88\x00\x00}\x00\x00d\x00\x00S'
f3-->co_consts          : (None,)
f3-->co_names           : ()
f3-->co_varnames        : ('n',)
f3-->co_freevars        : ('m',)
f3-->co_cellvars        : ()
f3-->co_filename        : pyvm_test4_function.py
f3-->co_name            : wapper
f3-->co_firstlineno     : 3
f3-->co_lnotab          : b'\x00\x01'

(9) co_lnotab: The relationship between bytecode instructions and source code line numbers in. pyc files

Object/lnotab_notes.txt:
All about co_lnotab, the line number table.
Code objects store a field named co_lnotab. This is an array > of unsigned bytes disguised as a Python string. It is used to map bytecode offsets to source code line #s for tracebacks and to identify line number boundaries for line tracing.
The array is conceptually a compressed list of (bytecode > offset increment, line number increment) pairs. The details > are important and delicate, best illustrated by example:

byte code offset source code line number
0 1
6 2
50 7
350 307
361 308

Instead of storing these numbers literally, we compress the list by storing only the increments from one row to the next. Conceptually, the stored list might look like:

0, 1, 6, 1, 44, 5, 300, 300, 11, 1 Formed arrays: 0, 1, (0 + 6), (1 + 1), (6 + 44), (2 + 5), (50 + 300), (7 + 300), (350 + 11), (307 + 1)

Reference:

[python Virtual Machine Running Principle] https://www.cnblogs.com/webber1992/p/6597166.html

Posted by raheel on Thu, 16 May 2019 01:57:35 -0700