1

When I compile this snippet with func1 I get an error about referencing output before assignment, which seems reasonable to me:

def func1():
    output += 1

output = 0
func1()
print(output)    

But when I compile this snippet with func2 I don't get an error, which seems unreasonable to me.

def func2():
    output.append(1)

output = []
func2()
print(output)

Thoughts? Thanks in advance and sorry if this is a duplicate. I didn't didn't see this particular question addressed in similarly titled posts when researching.

1
  • Try to make the variable output global. Otherwise, the function cannot access it Commented Dec 13, 2018 at 5:41

2 Answers 2

1

The problem here is in how Python binds names to variables.

Any time you write an assignment in a function (something like a = b or a += b), Python will bind that name locally for the entire function scope. This means that any variables with that name declared outside the function are ignored.

For example:

a = 1 # This variable is ignored.
def foo():
    print(a) # "a" hasn't been defined yet!
    a = 2 # This causes "a" to bind to a local variable.

This will produce an UnboundLocalError at the print statement because Python sees that a is assigned to later in the function, and binds it locally, ignoring the variable you defined outside the function. Since a is only defined after the print statement, you get an error.

This can be very confusing because removing the a += 2 line will cause the print statement to work as expected!

The solution is to explicitly tell Python not to bind the name locally. This can be done with the global and nonlocal keywords, e.g.:

a = 1
def foo():
    nonlocal a # or: global a
    print(a)
    a += 2

global tells Python to bind the name in the module's global scope, while nonlocal (introduced in Python 3) tells Python to bind to the enclosing scope (for example, if you define a function inside a function).

There's a nice explanation of this (with examples) in the documentation :-)


Once we understand these rules, we can see that your first example fails because you're trying to increment a variable that doesn't yet exist. output += 1 is equivalent to output = output + 1, which triggers local name binding.

On the other hand, your second example doesn't trigger the error because you're not assigning to output (you're mutating it instead), so it will bind to the global variable that you defined.

Sign up to request clarification or add additional context in comments.

Comments

0

In Python, global variables when called in functions don't work all the same.

The data types (on a high level) in Python can be categorised into "mutable" and "immutable".

  • If you have have mutable datatypes like list then they can be both : accessed and modified in the function without referring them with a global keyword.

Eg:

l = []
def foo():
    l.append(1) #works

foo()
print(l) # prints [1], so foo() changed the global list l.
  • But if you have immutable types like int or str then they can be accessed but not modified.

Eg:

someString = "abcdef"

def foo():
    print(someString[2]) # prints "c"
    someString += "l"    # error as someString cannot be modified unless you include it in the function foo() with the global keyword as shown below.
  • But to modify a global variable of immutable types like str you have to include it first with the global keyword then you're free to use it like any local variable.

Eg:

someString = "abcdef"

def foo():
    global someString    # This now includes someString in foo() scope and is allowed to be modified.

    print(someString[2]) # prints "c"

    someString += "l"    # works as expected

    print(someString) # prints "abcdefl"
  • Thanks to Mark Tolonen for this valuable point. At the end, you cannot reassign a global variable without the global keyword, irrespective of whether the variable is mutable or immutable.

Eg:

someInt  = 1
someStr  = "abc"
someList = [1,2,3]

def foo():
    someInt  += 3     # error, local variable referenced before assignment.
    someStr  += "def" # error, local variable referenced before assignment.
    someList += [4]   # error,  local variable referenced before assignment. Note that this is **not** the same as doing someList.append(4).

To make the above work, include them in the function via global keyword and use them as required.

3 Comments

Terminology nitpick...you can't modify an immutable variable. You can't reassign a name in the global namespace in a local function without the global keyword, regardless if it is mutable or immutable.
@MarkTolonen Thank you for pointing that out. Added the changes in the edit.
I'm afraid this has nothing to do with mutability/immutability. The problem is really about how Python binds names in function scope.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.