CPython Internals

CamPUG
19 Feb 2020

Viren Nadkarni

About

Software Engineer at Speechmatics

https://nadk.xyz
viren_ndk
viren-nadkarni

Compilation
foo.py

          a = 42
          b = 3.1415

          print(a + b)
          
Run foo.py
           
          $ python3 foo.py
          45.14
          
Compiling
           
          $ python3 -m py_compile foo.py

          $ ls __pycache__/
          foo.cpython-37.pyc

          $ python3 __pycache__/foo.cpython-37.pyc
          45.14
          
What is the pyc file
           
          $ file __pycache__/foo.cpython-37.pyc
          __pycache__/foo.cpython-37.pyc: data
          
          $ xxd __pycache__/foo.cpython-37.pyc
          420d 0d0a 0000 0000 3a13 515e 1d00 0000  B.......:.Q^....
          e300 0000 0000 0000 0000 0000 0003 0000  ................
          0040 0000 0073 1800 0000 6400 5a00 6401  .@...s....d.Z.d.
          5a01 6502 6501 6500 1700 8301 0100 6402  Z.e.e.e.......d.
          5300 2903 e92a 0000 0067 1f85 eb51 b81e  S.)..*...g...Q..
          0940 4e29 03da 0161 da01 62da 0570 7269  .@N)...a..b..pri
          6e74 a900 7205 0000 0072 0500 0000 fa06  nt..r....r......
          666f 6f2e 7079 da08 3c6d 6f64 756c 653e  foo.py..<module>
          0100 0000 7304 0000 0004 0104 01         ....s........
          
Interpreter

          $ python3
          Python 3.7.5 (default, Nov  1 2019, 02:16:32)
          >>> src = open('foo.py', 'r').read()
          >>> c = compile(src, 'foo.py', 'exec')
          >>> help('compile')
          compile(source, filename, mode, ...)
          Compile source into a code object that can be executed by
          exec() or eval().
          ...
          >>> c
          <code object <module> at 0x10751e0, file "foo.py", line 1>
          
Code Objects

          >>> dir(c)
          ['__class__', '__delattr__', '__dir__', '__doc__',
          ...
          'co_argcount', 'co_cellvars', 'co_code', 'co_consts',
          'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars',
          'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names',
          'co_nlocals', 'co_stacksize', 'co_varnames']
          
Code Instructions

          >>> c.co_code
          b'd\x00Z\x00d\x01Z\x01e\x02e\x01e\x00\x17\x00\x83\x01\x01\x00d\x02S\x00'
          >>> [ _ for _ in c.co_code ]
          [100, 0, 90, 0, 100, 1, 90, 1, 101, 2, 101, 1, 101, 0, 23,
          0, 131, 1, 1, 0, 100, 2, 83, 0]
          
Disassembly

          >>> import dis
          >>> dis.dis(c)
          1   0 LOAD_CONST               0 (42)
              2 STORE_NAME               0 (a)
          2   4 LOAD_CONST               1 (3.14)
              6 STORE_NAME               1 (b)
          3   8 LOAD_NAME                2 (print)
             10 LOAD_NAME                1 (b)
             12 LOAD_NAME                0 (a)
             14 BINARY_ADD
             16 CALL_FUNCTION            1
             18 POP_TOP
             20 LOAD_CONST               2 (None)
             22 RETURN_VALUE
          

          >>> [ _ for _ in c.co_code ]
          [100, 0, 90, 0, 100, 1, 90, 1, 101, 2, 101, 1, 101, 0, 23,
          0, 131, 1, 1, 0, 100, 2, 83, 0]
          >>> c.co_consts
          (42, 3.14, None)
          >>> c.co_names
          ('a', 'b', 'print')
          >>> exec(c)
          45.14
          
Back to pyc