Skip to content

Instantly share code, notes, and snippets.

@derrickturk
Created December 30, 2020 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save derrickturk/b38ef07e15603a17d8f602cffd55b1e8 to your computer and use it in GitHub Desktop.
Save derrickturk/b38ef07e15603a17d8f602cffd55b1e8 to your computer and use it in GitHub Desktop.
Python - the devil is in the details
Python 3.6.6 (v3.6.6:4cf1f54eb7, Jun 27 2018, 03:37:03) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> def f_list():
... xs = list()
... for i in range(10):
... xs.append(i * i)
... return xs
...
>>> def f_comp():
... return [i * i for i in range(10)]
...
>>> f_list()
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> f_comp()
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# the function using append in a loop maps almost 1:1 onto Python bytecode
>>> dis.dis(f_list)
2 0 LOAD_GLOBAL 0 (list)
2 CALL_FUNCTION 0
4 STORE_FAST 0 (xs)
3 6 SETUP_LOOP 30 (to 38)
8 LOAD_GLOBAL 1 (range)
10 LOAD_CONST 1 (10)
12 CALL_FUNCTION 1
14 GET_ITER
>> 16 FOR_ITER 18 (to 36)
18 STORE_FAST 1 (i)
4 20 LOAD_FAST 0 (xs)
22 LOAD_ATTR 2 (append)
24 LOAD_FAST 1 (i)
26 LOAD_FAST 1 (i)
28 BINARY_MULTIPLY
30 CALL_FUNCTION 1
32 POP_TOP
34 JUMP_ABSOLUTE 16
>> 36 POP_BLOCK
5 >> 38 LOAD_FAST 0 (xs)
40 RETURN_VALUE
# the function using a comprehension does indeed compile to fewer instructions...
>>> dis.dis(f_comp)
2 0 LOAD_CONST 1 (<code object <listcomp> at 0x000001AA3E479A50, file "<stdin>", line 2>)
2 LOAD_CONST 2 ('f_comp.<locals>.<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (range)
8 LOAD_CONST 3 (10)
10 CALL_FUNCTION 1
12 GET_ITER
14 CALL_FUNCTION 1
16 RETURN_VALUE
# ...but the piper has to be paid somewhere.
# Here we see that the "guts" of the list comprehension have themselves been compiled to a function.
# We do, however, get to use the LIST_APPEND instruction directly rather than calling the list.append function in a loop, and this avoids some overhead. (One function call [the list comprehension] to a function with a loop, vs. many function calls [to append] in a loop.)
>>> f_comp.__code__.co_consts
(None, <code object <listcomp> at 0x000001AA3E479A50, file "<stdin>", line 2>, 'f_comp.<locals>.<listcomp>', 10)
>>> dis.dis(f_comp.__code__.co_consts[1])
2 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 12 (to 18)
6 STORE_FAST 1 (i)
8 LOAD_FAST 1 (i)
10 LOAD_FAST 1 (i)
12 BINARY_MULTIPLY
14 LIST_APPEND 2
16 JUMP_ABSOLUTE 4
>> 18 RETURN_VALUE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment