1

I am using a list comprehension to assign a value to a class attribute in Python2.7. I noticed that this adds the variable used in the list comprehension as a class attribute.

class MyClass:
    some_strings = [s for s in ('Foo','Bar')]

print MyClass.s

Output: 'Bar' #??

Can someone explain why this is happening? Is there anything wrong with using list-comprehension there?

Jacques Gaudin
  • 15,779
  • 10
  • 54
  • 75

2 Answers2

2

There is nothing wrong. Using a list comprehension adds its variable to the local scope, just as a for loop would. When used inside a class definition, said local scope is used to initialize the class attributes. You have to del the name s if you don't want it in your class.

class MyClass:
  some_strings = [s for s in ('Foo','Bar')]
  del s

Note that in Python 3, the list comprehension will not add its variable to the local scope.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • 1
    I didn't know that. I am translating some Python3 code to Python2 and I had a strange bug which was due to an attribute being overriden by the variable in the list comprehension. Python3's behaviour is definitely a sensible improvement! I am surprised I never saw that in the common gotchas of Python2... – Jacques Gaudin Jul 01 '16 at 14:56
  • @JacquesGaudin I was actually surprised when I encountered the opposite effect - something not being overwritten when I expected it to. Was kinda hacky... I agree that the behavior in Py3 feels cleaner and generally encourages better code. – MisterMiyagi Jul 01 '16 at 15:47
1

Explaination from Guido Van Rossum

We also made another change in Python 3, to improve equivalence between list comprehensions and generator expressions. In Python 2, the list comprehension "leaks" the loop control variable into the surrounding scope: x = 'before' a = [x for x in 1, 2, 3] print x # this prints '3', not 'before'

This was an artifact of the original implementation of list comprehensions; it was one of Python's "dirty little secrets" for years. It started out as an intentional compromise to make list comprehensions blindingly fast, and while it was not a common pitfall for beginners, it definitely stung people occasionally. For generator expressions we could not do this. Generator expressions are implemented using generators, whose execution requires a separate execution frame. Thus, generator expressions (especially if they iterate over a short sequence) were less efficient than list comprehensions.

However, in Python 3, we decided to fix the "dirty little secret" of list comprehensions by using the same implementation strategy as for generator expressions. Thus, in Python 3, the above example (after modification to use print(x) :-) will print 'before', proving that the 'x' in the list comprehension temporarily shadows but does not override the 'x' in the surrounding scope.

Jacques Gaudin
  • 15,779
  • 10
  • 54
  • 75