-1

When trying out code that assigns a GUID to class instances, I wrote something similar to the following:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
x_id = 0 # immutable
x_id_list = [0] # mutable
def fx(x):
    global x_id # Disable to get "UnboundLocalError: local variable 'x_id' referenced before assignment"
    if x is None:
        x_id += 2
        x_id_list[0] += 2
    else:
        x_id += 1
        x_id_list[0] += 1
        return (x_id - 1)
    return x_id

expected = [x for x in xrange(10)]
actual = [fx(x) for x in expected]
assert(expected == actual), "expected = {}, actual = {}".format(expected, actual)
print x_id_list
print x_id

Notice that only the immutable x_id throws the UnboundLocalError if its global scope is not defined, but the mutable x_id_list continues to work fine without its global scope needing to be defined.

Why is that?

Zizouz212
  • 4,908
  • 5
  • 42
  • 66
PoorLuzer
  • 24,466
  • 7
  • 31
  • 35
  • I don't get an exception (and it shouldn't raise one either). Your program outputs `[10]` and `10` for me. – Zizouz212 Dec 03 '15 at 00:55
  • @Zizouz212 You need to comment out the `global x_id` line to get the error. – dimo414 Dec 03 '15 at 00:57
  • Possible duplicate of [Assigning to variable from parent function: "Local variable referenced before assignment"](http://stackoverflow.com/questions/8934772/assigning-to-variable-from-parent-function-local-variable-referenced-before-as) – Zizouz212 Dec 03 '15 at 01:08
  • If you do `x_id_list += [2]`, you'll get an UnboundLocalError for that, too. It's not an issue of mutability. – user2357112 Dec 03 '15 at 01:16

2 Answers2

0

The issue is not that x_id is immutable (it isn't - the integer value 0 is what's immutable), but that you cannot assign to a variable defined outside a function without explicitly declaring your intent to do so via global. Mutating the list x_id_list refers to does not change the value of the x_id_list variable itself, and therefore is permitted.

If you tried to do x_id_list += [1] you'd run into the same error. It is assigning to a variable, not mutating a value, that is the problem here.

From the docs:

In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.

Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.

This answer also goes into some more detail.

Community
  • 1
  • 1
dimo414
  • 47,227
  • 18
  • 148
  • 244
  • Good point about "Mutating the list x_id_list refers to does not change the value of the x_id_list". Some references would be good. – PoorLuzer Dec 03 '15 at 01:06
-1

That's because global scope variables are read-only in functions.

Since you're modifying the variable x_id, you need to reference it before using it, or Python will try to create a new variable that is local to the function.

Having used the global expression at the beginning of your function, you've told Python that you need to reference and modify that variable in your function. Since you are modifying the variable x_id, this happens. Since x_id_list is mutable, you are essentially not modifying it visibly (only internally, unlike x_id, where such a change is external), and therefore, you don't need the global keyword.

Zizouz212
  • 4,908
  • 5
  • 42
  • 66