Created
December 30, 2020 16:29
-
-
Save derrickturk/b38ef07e15603a17d8f602cffd55b1e8 to your computer and use it in GitHub Desktop.
Python - the devil is in the details
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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