Python: infos about the implementation of a Python function

I'm discovering the CPython implementation, the structure of Python objects and the Python bytecodes.

Playing with functions, I've found out that empty functions have a stack size of 1. Why? What var is declared to occupy the stack space?

Empty function:

def empty():
    pass

Function infos:

>>> dis.show_code(empty)

Name:                empty
Filename:            <pyshell#27>
Argument count:      0
Kw-only arguments:   0
Stack size:          1
Number of locals:    0
Variable names:
Constants:
    0: None

Names:
Flags:               OPTIMIZED, NEWLOCALS, NOFREE
First line number:   1
Free variables:
Cell variables:

Function with locals:

def withlocals():
    first = 0
    second = [1, 2, 3]

Function infos:

>>> dis.show_code(withlocals)

Name:                withlocals
Filename:            <pyshell#27>
Argument count:      0
Kw-only arguments:   0
Stack size:          3
Number of locals:    2
Variable names:
    0: first
    1: second

Constants:
    0: None
    1: 0
    2: 1
    3: 2
    4: 3

Names:
Flags:               OPTIMIZED, NEWLOCALS, NOFREE
First line number:   1
Free variables:
Cell variables:

Answers


The stack_size is the upper bound of the stack usage by the interpreter opcodes. However, the analysis has some bugs and another, larger one at the end of this post, so the bound is not tight.

>>> def empty():
...     pass
... 
>>> import dis
>>> dis.dis(empty)
  2           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE        

An empty function returns None. It requires 1 item of stack to load the reference to None on top of stack; RETURN_VALUE returns the value that is stored on top of stack.

The local variables themselves are not included in this count, which is very evident from

>>> def many_vars():
...     a = 1
...     b = 2
...     c = 3
...     d = 4
...     e = 5
...     f = 6
...     g = 7
... 
>>> many_vars.__code__.co_stacksize
1

In the case of

def withlocals():
    first = 0
    second = [1, 2, 3]

the stack must be large enough to build the list of 3. If you add elements to the list, the stack grows by that amount. I've added the size of the stack at each point to the dump:

>>> dis.dis(withlocals)
  2           0 LOAD_CONST               1 (0)          1
              3 STORE_FAST               0 (first)      0

  3           6 LOAD_CONST               2 (1)          1
              9 LOAD_CONST               3 (2)          2
             12 LOAD_CONST               4 (3)          3
             15 BUILD_LIST               3              1
             18 STORE_FAST               1 (second)     0
             21 LOAD_CONST               0 (None)       1
             24 RETURN_VALUE                            0

However the analysis seems to have bugs when it comes to tuple constants:

>>> def a_long_tuple():
...     first = (0, 0, 0, 0, 0, 0, 0)
... 
...
>>> dis.dis(a_long_tuple)
  2           0 LOAD_CONST               2 ((0, 0, 0, 0, 0, 0, 0))
              3 STORE_FAST               0 (first)
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE        
>>> dis.show_code(a_long_tuple)
Name:              withlocals
Filename:          <stdin>
Argument count:    0
Kw-only arguments: 0
Number of locals:  1
Stack size:        7
Flags:             OPTIMIZED, NEWLOCALS, NOFREE
Constants:
   0: None
   1: 0
   2: (0, 0, 0, 0, 0, 0, 0)
Variable names:
   0: first

The code only has one tuple, that is a constant, yet the analysis claims it requires stack space of 7, in both Python 2 and 3!

The reason for that is that the assembled code for building a constant tuple is initially identical to building a list, except with BUILD_TUPLE opcode at the end; but the peephole optimizer optimizes that into LOAD_CONST from partial assembler output. However the co_stacksize is calculated based on the original assembled code!


Need Your Help

problem about sqlite database, no such table:

android sqlite

I open a sqlite database and use rawquery to get data.

selectedSegmentIndex not updating UISegmentControl

iphone uisegmentedcontrol

In my viewDidLoad, I'm retrieving user preferences and updating the settings tab. It is working for editSelection (see code) but the other