Важные вещи, чтобы понять здесь
выражения генератор будет создавать объекты внутри функции, но список понимание не будет.
оба они свяжут переменную цикла с значениями, и переменные цикла будут в текущей области, если они еще не созданы.
Позволяет увидеть байтовые коды выражения генератора
>>> dis(compile('(i(0) + i(1) for a in alist)', 'string', 'exec'))
1 0 LOAD_CONST 0 (<code object <genexpr> at ...>)
3 MAKE_FUNCTION 0
6 LOAD_NAME 0 (alist)
9 GET_ITER
10 CALL_FUNCTION 1
13 POP_TOP
14 LOAD_CONST 1 (None)
17 RETURN_VALUE
Он загружает код объекта, а затем это делает его функцией. Позволяет увидеть фактический объект кода.
>>> dis(compile('(i(0) + i(1) for a in alist)', 'string', 'exec').co_consts[0])
1 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 27 (to 33)
6 STORE_FAST 1 (a)
9 LOAD_GLOBAL 0 (i)
12 LOAD_CONST 0 (0)
15 CALL_FUNCTION 1
18 LOAD_GLOBAL 0 (i)
21 LOAD_CONST 1 (1)
24 CALL_FUNCTION 1
27 BINARY_ADD
28 YIELD_VALUE
29 POP_TOP
30 JUMP_ABSOLUTE 3
>> 33 LOAD_CONST 2 (None)
36 RETURN_VALUE
Как вы видите здесь, текущее значение из итератора хранится в переменной a
. Но так как мы делаем этот объект функции, создаваемый a
будет виден только внутри выражения генератора.
Но в случае списка понимания,
>>> dis(compile('[i(0) + i(1) for a in alist]', 'string', 'exec'))
1 0 BUILD_LIST 0
3 LOAD_NAME 0 (alist)
6 GET_ITER
>> 7 FOR_ITER 28 (to 38)
10 STORE_NAME 1 (a)
13 LOAD_NAME 2 (i)
16 LOAD_CONST 0 (0)
19 CALL_FUNCTION 1
22 LOAD_NAME 2 (i)
25 LOAD_CONST 1 (1)
28 CALL_FUNCTION 1
31 BINARY_ADD
32 LIST_APPEND 2
35 JUMP_ABSOLUTE 7
>> 38 POP_TOP
39 LOAD_CONST 2 (None)
42 RETURN_VALUE
Там не создается явная функция и переменная a
создается в текущей области. Таким образом, a
просочился в текущую область действия.
При таком понимании давайте подходим к вашей проблеме.
>>> i = lambda x: a[x]
>>> alist = [(1, 2), (3, 4)]
Теперь, когда вы создаете список с пониманием,
>>> [i(0) + i(1) for a in alist]
[3, 7]
>>> a
(3, 4)
вы можете увидеть, что a
просочилась в текущей области и он по-прежнему связан с последним значением от итерации.
Итак, когда вы повторяете выражение генератора после понимания списка, функция lambda
использует пропущенный a
. Вот почему вы получаете [7, 7]
, так как a
по-прежнему привязан к (3, 4)
.
Но если вы сначала итерации выражения генератора, то a
будет привязан к значениям от alist
и не будет просочиться в текущую область, поскольку выражение генератора станет функцией. Итак, когда функция lambda
пытается получить доступ к a
, она не может найти ее где-либо. Вот почему он терпит неудачу с ошибкой.
Примечание: Такое же поведение не наблюдается в Python 3.x, поскольку утечка предотвращается путем создания функций для понимания списков. Возможно, вы захотите прочитать об этом в журнале «История Python», From List Comprehensions to Generator Expressions, написанном самим Гвидо.
он показывает ошибку для меня. –
@AvinashRaj Запуск второго метода сначала дает 'NameError: глобальное имя 'a' не определен' –
Там лежит ваша проблема, a определяется в первом как (3, 4), тогда функция list() всегда принимает это 'a' – TheGeorgeous