Кто-то писал: "" "Python достаточно умен. Пока объект, который вы перебираете, имеет метод __len__
или __length_hint__
, Python будет вызывать его для определения размера и предварительного выделения массива." " «
Насколько я могу сказать, нет предраспределения в понимании списка . Python не может сказать, исходя из размера INPUT, каким будет размер OUTPUT.
Посмотрите на этот код Python 2.6:
>>> def foo(func, iterable):
... return [func(i) for i in iterable]
...
>>> import dis; dis.dis(foo)
2 0 BUILD_LIST 0 #### build empty list
3 DUP_TOP
4 STORE_FAST 2 (_[1])
7 LOAD_FAST 1 (iterable)
10 GET_ITER
>> 11 FOR_ITER 19 (to 33)
14 STORE_FAST 3 (i)
17 LOAD_FAST 2 (_[1])
20 LOAD_FAST 0 (func)
23 LOAD_FAST 3 (i)
26 CALL_FUNCTION 1
29 LIST_APPEND #### stack[-2].append(stack[-1]); pop()
30 JUMP_ABSOLUTE 11
>> 33 DELETE_FAST 2 (_[1])
36 RETURN_VALUE
Он просто создает пустой список и добавляет то, что обеспечивает итерация.
Теперь посмотрите на этот код, который имеет «если» в понимании списка:
>>> def bar(func, iterable):
... return [func(i) for i in iterable if i]
...
>>> import dis; dis.dis(bar)
2 0 BUILD_LIST 0
3 DUP_TOP
4 STORE_FAST 2 (_[1])
7 LOAD_FAST 1 (iterable)
10 GET_ITER
>> 11 FOR_ITER 30 (to 44)
14 STORE_FAST 3 (i)
17 LOAD_FAST 3 (i)
20 JUMP_IF_FALSE 17 (to 40)
23 POP_TOP
24 LOAD_FAST 2 (_[1])
27 LOAD_FAST 0 (func)
30 LOAD_FAST 3 (i)
33 CALL_FUNCTION 1
36 LIST_APPEND
37 JUMP_ABSOLUTE 11
>> 40 POP_TOP
41 JUMP_ABSOLUTE 11
>> 44 DELETE_FAST 2 (_[1])
47 RETURN_VALUE
>>>
Тот же код, плюс некоторый код, чтобы избежать LIST_APPEND.
В Python 3.X вам нужно копать немного глубже:
>>> import dis
>>> def comprehension(f, iterable): return [f(i) for i in iterable]
...
>>> dis.dis(comprehension)
1 0 LOAD_CLOSURE 0 (f)
3 BUILD_TUPLE 1
6 LOAD_CONST 1 (<code object <listcomp> at 0x00C4B8D
8, file "<stdin>", line 1>)
9 MAKE_CLOSURE 0
12 LOAD_FAST 1 (iterable)
15 GET_ITER
16 CALL_FUNCTION 1
19 RETURN_VALUE
>>> dis.dis(comprehension.__code__.co_consts[1])
1 0 BUILD_LIST 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 18 (to 27)
9 STORE_FAST 1 (i)
12 LOAD_DEREF 0 (f)
15 LOAD_FAST 1 (i)
18 CALL_FUNCTION 1
21 LIST_APPEND 2
24 JUMP_ABSOLUTE 6
>> 27 RETURN_VALUE
>>>
Это та же старая схема: начните с создания пустого списка, затем итерируйте итерируемый, добавляя в список по мере необходимости. Я не вижу здесь никакого предварительного распределения.
Оптимизация, о которой вы думаете, используется внутри одного кода операции, например реализация list.extend(iterable)
может предварительно выделить, если iterable
может точно сообщить его длину. list.append(object)
дается один объект, а не повторяемый.