Important things to understand here are

generator expressions will be creating function objects internally but list comprehension will not. they both will bind the loop variable to the values and the loop variables will be in the current scope if they are not already created.

Lets see the byte codes of the generator expression

>>> 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

It loads the code object and then it makes it a function. Lets see the actual code object.

>>> 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

As you see here, the current value from the iterator is stored in the variable a . But since we make this a function object, the a created will be visible only within the generator expression.

But in case of list comprehension,

>>> 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

There is no explicit function creation and the variable a is created in the current scope. So, a is leaked in to the current scope.

With this understanding, lets approach your problem.

>>> i = lambda x: a[x] >>> alist = [(1, 2), (3, 4)]

Now, when you create a list with comprehension,

>>> [i(0) + i(1) for a in alist] [3, 7] >>> a (3, 4)

you can see that a is leaked to the current scope and it is still bound to the last value from the iteration.

So, when you iterate the generator expression after the list comprehension, the lambda function uses the leaked a . That is why you are getting [7, 7] , since a is still bound to (3, 4) .

But, if you iterate the generator expression first, then the a will be bound to the values from alist and will not be leaked to the current scope as generator expression becomes a function. So, when the lambda function tries to access a , it couldn't find it anywhere. That is why it fails with the error.

Note: The same behaviour cannot be observed in Python 3.x, because the leaking is prevented by creating functions for list comprehensions as well. You might want to read more about this in the History of Python blog's post, From List Comprehensions to Generator Expressions, written by Guido himself.