Saltycrane logo

SaltyCrane Blog

Notes on Python, Django, and web development on Ubuntu Linux

    

Notes on Python variable scope

Example 1: The difference between global and local variables

Global variables are accessible inside and outside of functions. Local variables are only accessible inside the function. In the example below, the function can access both the global and the local variable. However, trying to access the local variable outside the function produces an error.
global_var = 'foo'
def ex1():
    local_var = 'bar'
    print global_var
    print local_var

ex1()
print global_var
print local_var  # this gives an error
foo
bar
foo
Traceback (most recent call last):
  File "nested_scope.py", line 12, in 
    print local_var  # this gives an error
NameError: name 'local_var' is not defined

Example 2: How *not* to set a global variable

*Setting* a global variable from within a function is not as simple. If I set a variable in a function with the same name as a global variable, I am actually creating a new local variable. In the example below, var remains 'foo' even after the function is called.
var = 'foo'
def ex2():
    var = 'bar'
    print 'inside the function var is ', var

ex2()
print 'outside the function var is ', var
inside the function var is  bar
outside the function var is  foo

Example 3: How to set a global variable

To set the global variable inside a function, I need to use the global statement. This declares the inner variable to have module scope. Now var remains 'bar' after the function is called.
var = 'foo'
def ex3():
    global var
    var = 'bar'
    print 'inside the function var is ', var

ex3()
print 'outside the function var is ', var
inside the function var is  bar
outside the function var is  bar

Example 4: Nested functions

Scoping for nested functions works similarly. In the example below, the inner function can access both var_outer and var_inner. However, the outer function cannot access var_inner. Side note: the inner function is considered a closure if it makes reference to a non-global outside variable.
def ex4():
    var_outer = 'foo'
    def inner():
        var_inner = 'bar'
        print var_outer
        print var_inner
    inner()
    print var_outer
    print var_inner # this gives an error

ex4()
foo
bar
foo
Traceback (most recent call last):
  File "nested_scope.py", line 53, in 
    ex3()
  File "nested_scope.py", line 51, in ex3
    print var_inner # this gives an error
NameError: global name 'var_inner' is not defined

Example 5: How *not* to set an outer variable

Like Example 2, setting a variable in the inner function creates a new local variable instead of modifying the outer variable. In the example below, var in the outer function does not get changed to 'bar'.
def ex5():
    var = 'foo'
    def inner():
        var = 'bar'
        print 'inside inner, var is ', var
    inner()
    print 'inside outer function, var is ', var

ex5()
inside inner, var is  bar
inside outer function, var is  foo

Example 6: Another way to *not* set an outer variable

However, using the global keyword won't work in this case. global cause a variable to have module scope, but I want my variable to have the scope of the outer function. Per the Python 3000 Status Update, Python 3000 will have a nonlocal keyword to solve this problem. See PEP 3104 for more information about nonlocal and nested scopes. In the example below, var is still not changed to 'bar' in the outer function.
def ex6():
    var = 'foo'
    def inner():
        global var
        var = 'bar'
        print 'inside inner, var is ', var
    inner()
    print 'inside outer function, var is ', var

ex6()
inside inner, var is  bar
inside outer function, var is  foo

Example 7: A workaround until Python 3000 arrives

A workaround is to create an empty class to use as an additional namespace. Now the variable in the outer function can be set to 'bar'.
class Namespace: pass
def ex7():
    ns = Namespace()
    ns.var = 'foo'
    def inner():
        ns.var = 'bar'
        print 'inside inner, ns.var is ', ns.var
    inner()
    print 'inside outer function, ns.var is ', ns.var
ex7()
inside inner, ns.var is  bar
inside outer function, ns.var is  bar

Example 8: Alternative to Example 7

Update 2010-03-01: According to Alexander's comment below, this is not a good way to do things.

I learned about this method from Nihiliad's comment on my recursion example. To me, this seems like a more elegant alternative to the solution in Example 7.

def ex8():
    ex8.var = 'foo'
    def inner():
        ex8.var = 'bar'
        print 'inside inner, ex8.var is ', ex8.var
    inner()
    print 'inside outer function, ex8.var is ', ex8.var
ex8()
inside inner, ex8.var is  bar
inside outer function, ex8.var is  bar

Reference

Core Python Programming, Second Edition, Ch 11

See also

Correcting ignorance: learning a bit about Ruby blocks by Nick Coghlan

26 Comments — feed icon Comments feed for this post


#1 Anonymous commented on 2008-03-20:

Many thanks for this post! I could not find the solution to setting global variables anywhere in all my python documentation.


#2 Anonymous commented on 2008-04-14:

Another workaround is to wrap the variable in a mutable:

def ex8():
... var = ['foo']
... def inner():
... var[0] = 'bar'
... print 'inside inner, var is ', var
... inner()
... print 'inside outer function, var is ', var
...
ex8()
inside inner, var is ['bar']
inside outer function, var is ['bar']


#3 Asif commented on 2008-05-15:

Can anybody tell me what's wrong in the below code:

argref_list = []
for entry in arg_l: # processing #nnnn($Fmm)
print argref_list
argref_list.append(decode_single_argument(entry.strip()))

the output is:
[]
NONE

note that 'NONE' is printed (strange) and secondly throws the error that NONETYPE has no append function.

I am fade up with PYTHON!

Please help!


#4 Anonymous commented on 2008-06-26:

Nice post, told me exactly what I needed to know!


#5 Brendan commented on 2009-02-26:

Thanks for the clear and concise examples. The python tutorial left me confused about variable scope. This cleared it up.


#6 wvh commented on 2009-03-25:

This confused me for a long time, but your explanation is understandable. Thanks very much!


#7 pythonkhmer commented on 2009-04-21:

It's clear explanation about variables scope.that python official documentation aborted these. thanks,thanks,for your sharing.


#8 roger.ngeuf commented on 2009-04-25:

Thanks a lot. I did wrote this code in my programm a wonder why he didn't work (I have hear that python support closure. my code was :

def main():
    count = 0
    def counting():
        for i in range(10):
            count += 1
    counting()
    return count

which throw an exception. if first change it to :

def main():
    count = 0
    def counting(count):
        for i in range(10):
            count += 1
    counting()
    return count

but thanks to you, i update it to :

def main():
    main.count = 0
    def counting():
        for i in range(10):
            main.count += 1
    counting()
    return main.count

Nice Post and keep posting stuff like this.


#9 roger.ngeuf commented on 2009-04-25:

Sorry but your editor didn't keep the formatting


#10 Eliot commented on 2009-04-25:

roger, Thanks for the comment. I fixed the formatting for your code. To format code blocks, put 4 spaces before each line and a blank line before and after the block.


#11 Oli commented on 2009-05-13:

Hi,

firstly great article, really really useful.

I was wondering... in a code I am writing, I assign a global variable a value from a function, but I then want to delete that global variable from another function in which identically named local variables are defined. For this reason, "del variable" deletes the local not the global. How would I go about deleting the global variable?

Many thanks,

Oli


#12 Zach commented on 2009-08-19:

Thanks, this was helpful.

I do have a question about scope within a class. I am using Python 2.5

def class foo(object):
    def test(self):
        test.var = 1
        print test.var

f = foo()
f.test()

Result is "global name test is not defined"

I can get around this by using a "namespace" or mutable wrapper as described but I was just curious why this didn't work


#13 Eliot commented on 2009-08-19:

Zach,
I think you get this error message because test is a method of foo and not a global. However, the following code gives an error as well:

class foo(object):
    def test(self):
        self.test.var = 1
        print self.test

f = foo()
f.test()

Error:

AttributeError: 'instancemethod' object has no attribute 'var'

#14 akaihola commented on 2009-10-23:

A variation of Example 7:

def ex7b():
    class ns: pass
    ns.var = 'foo'
    def inner():
        ns.var = 'bar'
        print 'inside inner, ns.var is ', ns.var
    inner()
    print 'inside outer function, ns.var is ', ns.var
ex7b()

#15 Eliot commented on 2009-10-23:

akaihola, thanks. it does seem to make more sense to put the namespace within the outer function. btw, nice blog-- i see we have some similar interests (twisted, scribus, emacs, django)


#16 Leo commented on 2010-02-24:

@Zach #12:

Hi Zach,

I think the snippet you posted is trying to access 'test', a method of 'foo', before its declaration is complete (within the same block), thus the error. Did you mean

self.var = 3

or maybe

foo.var = 3

#17 Leo commented on 2010-02-24:

Can someone help me understand what is wrong with this example?

class T:
  A = range(2)
  B = range(4)
  s = sum(i*j for i in A for j in B)

It produces the exception:

<type 'exceptions.NameError'>: global name 'j' is not defined

The exception above is especially confusing when the following similar example (I just replaced the generator by an explicit array) works:

class T:
  A = range(2)
  B = range(4)
  s = sum([(i*j) for i in A for j in B])

(BTW, the class scope declarations are intentional).

Thanks, Leo.


#18 Alexander Khodyrev commented on 2010-02-28:

Example 8 is not a very good way to do things. ex8's properties are effectively global variables. That means that something like http://www.paulgraham.com/accgen.html won't work this way. (inner functions defined this way are not closures)


#19 Eliot commented on 2010-03-01:

Leo: sorry, I've run in to this also, but don't know the answer.

Alexander: thank you for alerting me to this. I added a quick note to example 8 above.


#20 Gerrat commented on 2010-03-01:

As for the question in #17, see: http://docs.python.org/reference/executionmodel.html#naming-and-binding

"A scope defines the visibility of a name within a block. If a local variable is defined in a block, its scope includes that block. If the definition occurs in a function block, the scope extends to any blocks contained within the defining one, unless a contained block introduces a different binding for the name. The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods – this includes generator expressions since they are implemented using a function scope. This means that the following will fail:"

class A:
    a = 42
    b = list(a + i for i in range(10))

#21 Beni Cherniavsky-Paskin commented on 2010-08-31:

#18: ex8 vars are indeed effectively global, but you can make it work if you use attributes of the inner function. Here is Paul Graham's accgen:

def foo(n):
    def inner(i):
        inner.n += i
        return inner.n
    inner.n = n
    return inner

>>> f = foo(10)
>>> print f(1), f(2), f(3)
11 13 16
>>> f = foo(20)
>>> print f(1), f(2), f(3)
21 23 26

#22 Sriram commented on 2012-03-13:

Really good. Thanks


#23 Rui commented on 2012-10-31:

Many thanks!


#24 Paul Jensen commented on 2013-01-04:

Thanks, this was very helpful.


#25 Elliott commented on 2013-03-26:

Thank you! This explained my closure woes.


#26 theKashyap commented on 2013-09-26:

Nice post.. gave me very useful info, not quite clear from python error messages.. But I find it incomplete as it's missing description of how scope works in classes. Thanks none the less..

Post a comment

Required
Required, but not displayed
Optional

Format using Markdown. (No HTML.)
  • Code blocks: prefix each line by at least 4 spaces or 1 tab (and a blank line before and after)
  • Code span: surround with backticks
  • Blockquotes: prefix lines to be quoted with >
  • Links: <URL>
  • Links w/ description: [description](URL)
Created with Django | Hosted by Linode