10

Okay, so I've had this really annoying problem where a variable got set locally but then outside of that function reverted to it's old self (in this case None), but at the same time I could manipulate other variables and still can without using the "global" keyword.

I can't provide the real code for this but it goes something like this:

foo = {}
foo_foo = {}
bar = None


def changes_foo():
    ...do some stuff to foo...

class EditThread(threading.Thread):

    def __init__(self):
        setup()

    def run(self):
       for key, value in foo.items():
           do_update_task(key, value)

    def do_update_task(self, key, value):
         ...do some editing too foo...
         del foo[key]
         bar = [key, value]
         foo_foo[key] = value

def print_the_bar():
    print bar 

Please note that all the operations on foo and foo_foo works just fine, but bar is still None when I call print_the_bar, and I've had lots of print statements in my code to verify that bar inside of do_update_task indeed has the correct values and isn't None.

Could someone please explain to me why it is so?

Santosh Kumar
  • 26,475
  • 20
  • 67
  • 118
Daniel Figueroa
  • 10,348
  • 5
  • 44
  • 66
  • related: http://stackoverflow.com/questions/423379/using-global-variables-in-a-function-other-than-the-one-that-created-them – SeanC Aug 08 '12 at 15:01
  • My point is that I can still make changes to both foo and foo_foo but when I make changes to bar it only recides in the local scope. I'm more than willing to accept that I'm not a seasoned pythonista, my question is why do I keep getting this error. – Daniel Figueroa Aug 08 '12 at 15:05
  • "Could someone please explain to me why it is so?" In two words: variable scoping. In more words: [link](http://docs.python.org/release/1.5.1p1/tut/scopes.html) – chucksmash Aug 08 '12 at 15:06
  • 2
    "when do I need a global variable?" -- Only when you need to rethink your program design. – mgilson Aug 08 '12 at 15:20
  • Yes yes, we all now that global variables are pure evil... :P – Daniel Figueroa Aug 08 '12 at 15:26
  • possible duplicate of [Do you use the "global" statement in Python?](http://stackoverflow.com/questions/146557/do-you-use-the-global-statement-in-python) – jamylak Aug 09 '12 at 11:43

4 Answers4

22

If you only need to read the value of a global variable in a function, you don't need the global keyword:

x = 3
def f():
    print x

If you ever set the value of a global variable, you need the keyword so that you don't create a local variable:

x = 3
def f():
    global x
    x = 5

f()
print x
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thanks, this difference was really revealing to me! – Alex Dec 20 '17 at 12:44
  • 2
    +1 for conciseness and clarity. Also see [this](https://stackoverflow.com/questions/14081308/why-is-the-global-keyword-not-required-in-this-case) on how this doesn't work as many would expect for dictionaries. – akhan Jul 13 '18 at 04:45
  • Understanding the difference between assigning to a *name* (`x = 5`) and mutation (often disguised as a assignment like `x[...] = 5`) is fundamental to understanding how Python works. – chepner Jul 13 '18 at 13:59
10

When you do things to foo and foo_foo, you're not changing the reference:

foo = {}
foo['key'] = 'stuff'

foo still refers to the same object as before; it just now contains more data.

bar = ['key', 'value']

This reassigns bar to refer to a new object (the list with two elements).

However, when that line is encountered inside a function, it creates a local reference bar unless you say global bar. In effect, you have two different variables named bar: the global and the local.

Saying global bar tells Python to use the global version of bar rather than creating a new local variable with the same name.

Generally, if you are modifying global variables, you should state global varname for each one to avoid accidentally creating a local.

Or, you can use a class:

class State(object):
    def __init__(self):
        self.foo = {}
        self.foo_foo = {}
        self.bar = None

state = State()

def fn():
    state.bar = ['key', 'value']
mrb
  • 3,281
  • 1
  • 20
  • 31
  • Thank you for a good answer that explains why. I will accept your answer as soon as i can. – Daniel Figueroa Aug 08 '12 at 15:09
  • 1
    +1 for the only answer that doesn't claim you can't modify global variables without the global keyword; of course you can modify them, you just can't globally re-bind the name with `=`. – Wooble Aug 08 '12 at 15:29
  • @Wooble Good point .. I think I will actually delete my answer just based on that :) – Levon Aug 08 '12 at 15:41
0

Variables you define in function are accessible only from this function's body.

Edward Ruchevits
  • 6,411
  • 12
  • 51
  • 86
0

You can read variables from the global scope from within a function, but you can't modify their values without using the "global" keyword.

When you attempt to set bar =[key, value], a new bar variable is added to your function's namespace. From then on, bar references this new variable, and not the global one.

Although you can read the global bar's value, once you attempt to re-assign it a value from within your function, you've actually created a new bar and are not acting on the global one.

bibs
  • 1,226
  • 2
  • 11
  • 14