Using a list comprehension to look up variables works with globals() but not locals(). Why?

Daniel C

I am updating a project from python 2.7 to python 3.6.

I have a list comprehension that looks up variables from locals which worked in python 2.7. It only works in python 3.6 when I switch to using globals.

Below is a toy example to illustrate the issue.

The relevant code is:

(A,B,C) = (1,2,3)
myvars = ['A','B','C']

If I execute the following code:

[locals().get(var) for var in myvars]

the return value in python 3.6 is:

[None, None, None]

However, the return value in python 2.7 is:

[1, 2, 3]

If I execute the following code using globals:

[globals().get(var) for var in myvars]

then I get the same result in python 2.7 and 3.6:

[1, 2, 3]

Can anyone explain why the code using locals() no longer works in python 3.6?

Joe Iddon

If we dissemble a more basic list-comprehension. we can see why this is happening:

>>> import dis
>>> dis.dis("[i for i in range(10)]")
  1           0 LOAD_CONST               0 (<code object <listcomp>)
              3 LOAD_CONST               1 ('<listcomp>')
              6 MAKE_FUNCTION            0
              9 LOAD_NAME                0 (range)
             12 LOAD_CONST               2 (10)
             15 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             18 GET_ITER
             19 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             22 RETURN_VALUE

So, on line 9, we see that a list-comp actually makes a function which is called (you may be more used to this when using generators). Therefore, the function would have its own set of local variables as it is in a separate scope - thus yielding this unexpected behaviour.


Furthermore, as pointed out by @juanpa, the "What's New in Python3" docs do explicitly state the following:

list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a list() constructor, and in particular the loop control variables are no longer leaked into the surrounding scope.


Note also that there is a bug page on this: https://bugs.python.org/issue21161, however juanpa has convinced me that this isn't a bug for the reason that it is clearly documented to work this way!

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Why does pdb give NameError for variables referenced in "if" clause of list comprehension?

multiple variables in list comprehension?

list comprehension in exec with empty locals: NameError

Is there a way to speed up this list comprehension?

List comprehension works but not for loop––why?

Tidying up a list comprehension in Haskell

Python - How to obtain a dictionary(or even a list) of variables in a specific scope; more specific than locals/globals()

C++ - Different concurrent behavious when using locals and globals

Using list comprehension with two variables in haskell

Error in list comprehension with `locals()` in python

Using List Comprehension to look up single values in a Dataframe

How does this code of list comprehension with multiple variables assigned works

locals() vs globals() in function

Globals and Locals

Using a data frame as a look up table for a list

Why is this list comprehension not working?

Why lambda function is not applicable in case of list comprehension, whereas some other user defined function works in the list comprehension?

Speeding up `any` with list comprehension

python list comprehension and multiple variables

How to find look up a list using a string?

python quits unexpectedly when using locals() or globals()

List comprehension works but not generator expression

Why is using list comprehension here so slow?

Why is this list comprehension slower than initializing conditionally using a for loop in Python?

Ways to speed up this list comprehension

scope of globals, locals, use with exec()

Terraform combine variables using locals

creating a list of functions using globals()

List comprehension returning two variables