I’ve just been bitten by scoping in Python. If you gave me this code and asked me what it did, I’d probably guess that it was a trick question and look carefully at it. What would you say?
number = 100
one = number / 100
numbers = [one, one+1, one+2]
big_numbers = [number * 100 for number in numbers]
bigger_numbers = [big_number * 100 for big_number in big_numbers]
It’s not obvious. You’d be forgiven for saying 100. Forgiven by me, unfortunately not forgiven by the Python interpreter. When you’re reading and writing code day to day you’re not always on the look-out for trick questions. The Python interpreter specialises in looking out for trick questions (aka correct behaviour), and would give you the correct answer of 3.
The mistake I made was to re-use a variable name in a list comprehension.
The problem here is that there is no extra scoping in the list comprehension. The variable number isn’t bound in some kind of magical special scope for the list comprehension. So the binding of number in the list comprehension is the same as number in the function closure scope. The value of number in the function closure is the last value it was assigned, in the last iteration of the loop. In many languages (Java, C#, C, Haskell, Scala) this wouldn’t happen and the function would print the number 100. This is a symptom of Python taking functional features (list comprehensions in this case) from purer languages without the attendant purity.
I’ve been writing Python for years and this still bit me. Look out, you may be the next victim.