2

I am writing program as below for testing my knowledge about Python Scope of variable

def show(a,b):
    def add():
        a = a+b #Error at this line "UnboundLocalError: local variable 'a' referenced before assignment" 
#I know we can use nonlocal a,b to avoid error 
    add()
    print("a=",a)
x=4
y=2
show(x,y)

Then I tried same program with some little changes with x and y as list. Code is as shown below

def show(a,b):
    def add():
        a[0] = a[0]+b[0] #No Error at this line
    add()
    print("a=",a[0])

x=[4]
y=[2]
show(x,y)

And this code runs fine. I am not getting this strange behavior in python.

krishna
  • 25
  • 4
  • 1
    The **type of the object is irrelevant with regards to scope**. *Rebinding a name*, regardless of what that name refers to, will always cause this unbound local error to occur – juanpa.arrivillaga Mar 02 '20 at 20:07
  • 1
    It's very important to keep in mind, Python *never treats objects differently* depending on their type. All objects follow the same semantics in the language. There is no special case treatment for mutable/immutable objects. Immutable objects **simply lack mutator methods**. – juanpa.arrivillaga Mar 02 '20 at 20:12
  • Does this answer your question? [Don't understand why UnboundLocalError occurs](https://stackoverflow.com/questions/9264763/dont-understand-why-unboundlocalerror-occurs) also https://stackoverflow.com/questions/370357/unboundlocalerror-on-local-variable-when-reassigned-after-first-use – Tomerikoo Mar 02 '20 at 20:17
  • 1
    [Facts and myths about Python names and values](https://people.cs.kuleuven.be/~danny.deschreye/Names_and_Values.html) – kaya3 Mar 02 '20 at 20:18

1 Answers1

2

Scoping rules are the same for all variables. It doesn't matter what objects they refer to.

However, a = a + b and a[0] = a[0] + b[0] are different things.

  1. The first one is assignment to a global variable that is actually treated as local by Python. Details can be found in this post: Python function global variables?

  2. The second one is syntactic sugar and doesn't do an assignment! It's desugared (translated internally) to a call to __setitem__:

    a.__setitem__(0, a[0] + b[0])
    

    As you can see, no assignment here, point (1) above doesn't apply, so no issues here.

In general, code like a[i] = b will be translated to a.__setitem__(i, b), and some other syntax is translated into function calls as well, like a + b can be a.__add__(b), a[0] is a.__getitem__(0) and so on.

So, a[0] = a[0] + b[0] will end up being this monstrosity:

a.__setitem__(0, a.__getitem__(0).__add__(b.__getitem__(0)))

No assignment - no problem with global variables suddenly turned local.

ForceBru
  • 43,482
  • 10
  • 63
  • 98
  • 1
    Nit with point 1: It's an *attempt* to assign to a global (or nonlocal?) variable, but without a `global` (or `nonlocal`) statement, it's an assignment to (and creation of) a local variable. – chepner Mar 02 '20 at 20:10
  • @chepner, I tried to convey this idea via "that is actually _treated as local_ by Python": the programmer knows that it's supposed to be global, but Python has no idea, as explained in the linked question. So yeah, an _attempt_ indeed. – ForceBru Mar 02 '20 at 20:12
  • Very minor technical point: `a[0] = ...` is not equivalent to `a.__setitem__(0, ...)`; it's more like `a.__class__.__setitem__(a, 0, ...)`. You can observe the difference if you declare `class A: pass` and then do `a = A(); a.__setitem__ = lambda *v: None`. Then `a[0] = 1` will raise an error but `a.__setitem__(0, 1)` will do nothing. – kaya3 Mar 02 '20 at 20:20