4

I was messing around with the scoping in python and found something that I think is rather strange:

g = 5

def foo(a):
    if a:
        global g
        g = 10
    else:
        g = 20


print("global g: ",g)

foo(False)
print("global g: ",g) # 20?! What?

foo(True)
print("global g: ",g)

My believe was that the second print should have been "5" since the global statement was never executed, but clearly, the output is 20(!).

What's the logic behind this?

jamylak
  • 128,818
  • 30
  • 231
  • 230
monoceres
  • 4,722
  • 4
  • 38
  • 63
  • If you're interested in playing around with this stuff, you should learn the basics of bytecode and play around with the `dis` module. (Try `dis.dis(foo)` after defining `foo` with `global` at the top level, inside `if`, and not there at all.) – abarnert May 12 '13 at 13:41
  • 1
    I actually did use the dis module so I saw that the g = 20 was put into the global scope. I therefore suspected the given answer, but wanted to make sure :) – monoceres May 12 '13 at 13:44

1 Answers1

7

The global keyword is used by the python compiler to mark a name in the function scope as global.

As soon as you use it anywhere in the function, that name is no longer a local name.

Note that if does not introduce a new scope, only functions and modules do (with classes, list, dict and set comprehensions being a special cases of function scopes).

A (hard to read and non-pythonic) work-around would be to use the globals() function:

def foo(a):
    if a:
        globals()['g'] = 10
    else:
        g = 20
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    It's also worth pointing out that in Python (unlike C and friends), an `if` block is not a new scope; the scope of that `global` statement is the function. – abarnert May 12 '13 at 13:40
  • Interesting. I would not expect this from a dynamic language like python. Is there any discussions on why this decision was made? – monoceres May 12 '13 at 13:42
  • @abarnert: indeed; expanded with what *does* create a new scope. – Martijn Pieters May 12 '13 at 13:43
  • 4
    @monoceres: The decision as to what kind of name each name is has to be made at function-definition time, or closures would be more complicated. (Look at what goes into `foo.func_code.co_names` vs. `co_varnames`, etc.) – abarnert May 12 '13 at 13:43
  • I see. I'm currently implementing a language of my own and facing the same problems (which is why I'm messing with python). The constant battle when implementing my scopes are between speedy lookups, runtime flexibility and dynamic rules. So I can understand why this was done. – monoceres May 12 '13 at 13:49
  • 1
    @monoceres: As for a discussion… Really, for everything that's been the same since Python 1.0, doesn't get argued about yearly on python-ideas or -dev, and isn't mentioned in the [Design FAQ](http://docs.python.org/2/faq/design.html), the "why" is sometimes hard to find… But that brings up an idea: Suggest changing it on python-ideas, and you'll get Guido and others telling you why it's a dumb idea. :) – abarnert May 12 '13 at 13:50
  • @monoceres: You might want to consider some way of making the `globals()['g']` solution less ugly in your language, so you don't need a `globals` declaration at all. That seems like the easiest way out of it. (You might also want to add some way to get at closure variables, which Python forgot about until 3.0, and still only has a partial solution for…) – abarnert May 12 '13 at 13:53
  • Unfortunetely I'm doing a clean-house implementation of a language where my example works like I thought it did in python so I don't have too much creative freedom. – monoceres May 12 '13 at 14:01