The basic understanding of the list is the following syntax
[expression for var in iterable]
When class understanding takes place inside the class, class attributes can be used in iterable . This is true in Python2 and Python3.
However, class attributes can be used (i.e. available) in expression in Python2, but not in Python3.
The story is slightly different for generator expressions:
(expression for var in iterable)
While class attributes are still available from iterable , class attributes are not available from expression . (This is true for Python2 and Python3).
This can be summarized as follows:
Python2 Python3 Can access class attributes -------------------------------------------------- list comp. iterable YY list comp. expression YN gen expr. iterable YY gen expr. expression NN dict comp. iterable YY dict comp. expression NN
(Dict insights behave the same as generator expressions in this regard.)
Now, how does this relate to your question:
In your example
second_d = dict((k,first_d[k]) for k in (2,3))
a NameError occurs because first_d not available from the expression part of the generator expression.
The workaround for Python2 is to change the generator expression to a list comprehension:
second_d = dict([(k,first_d[k]) for k in (2,3)])
However, I do not consider this a very convenient solution, since this code will not work in Python3.
You can do as Joel Cornett suggests:
second_d = {k: v for k, v in first_d.items() if k in (2, 3)}
since it uses first_d in iterable and not in the expression part of the dict understanding. But this may take more elements than necessary if first_d contains many elements. Nevermhess, this solution can be just fine if first_d small.
In general, you can avoid this problem by specifying a helper function that can be defined inside or outside the class:
def partial_dict(dct, keys): return {k:dct[k] for k in keys} class Example(object): first_d = {1:1,2:2,3:3,4:4} second_d = partial_dict(first_d, (2,3)) class Example2(object): a = [1,2,3,4,5] b = [2,4] def myfunc(A, B): return [x for x in A if x not in B] c = myfunc(a, b) print(Example().second_d)
Functions work because they define a local region and variables in this local region can be accessed from within the understanding of dict.
This is explained here but it is not entirely convenient for me, because it does not explain why the expression part behaves differently than the iterable list comprehension part, generator expression or concept understanding.
Thus, I cannot explain (completely) why Python behaves this way, just because it behaves that way.