0

Below program asks for nonlocal keyword with UnboundLocalError: local variable 'balance' referenced before assignment

>>> def make_withdraw(balance):
    """Return a withdraw function with a starting balance."""
    def withdraw(amount):
        if amount > balance:
            return 'Insufficient funds'
        balance = balance - amount
        return balance
    return withdraw

>>> withdraw = make_withdraw(101)
>>> withdraw(25)

But, below program does not give such error when inner function shift refers to lst before assignment as temp = lst[0].

def shift_left(lst, n):
    """Shifts the lst over by n indices

    >>> lst = [1, 2, 3, 4, 5]
    >>> shift_left(lst, 2)
    >>> lst
    [3, 4, 5, 1, 2]
    """
    assert (n > 0), "n should be non-negative integer"
    def shift(ntimes):
        if ntimes == 0:
            return
        else:
            temp = lst[0]
            for index in range(len(lst) - 1):
                lst[index] = lst[index + 1]         
            lst[index + 1] = temp
            return shift(ntimes-1)
    return shift(n)

How do I understand/compare these two scenarios?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
overexchange
  • 15,768
  • 30
  • 152
  • 347

1 Answers1

1

You are never assigning to lst, only to lst[index]. The two concepts are not quite the same thing.

The line:

lst = some_other_value

would rebind the name lst to point to another object. The line:

lst[index] = some_other_value

alters the object referenced by the name lst, by binding a specific index in that sequence to something else. The name lst itself is never altered, so there is no ambiguity here as to what scope that name belongs in.

In Python scoping, only binding operations on the name itself count. Binding operations are not just (direct) assignments, but also include function parameters, function and class definitions, import statements and targets for except .. as, with .. as and the target in for ... in loops. If a name is bound to in a given scope, it is considered local, in all other cases Python looks for the name in a parent scope, with the outermost scope being global.

Assignments to subscriptions (using [...]) are not binding operations in this context.

See the Naming and binding section of the Python Execution Model documentation, as well as the Short Description of the Scoping Rules? post.

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • sorry I have edited my question, because accessign index in not the right example!!! am talking about referring a variable before assignment. In first case, it is `balance` and second case it is `lst`. – overexchange May 22 '15 at 13:43
  • @overexchange: that is not very helpful, because your question is now simply a duplicate. `lst1` is not non-local because you assigned to it. – Martijn Pieters May 22 '15 at 13:44
  • @overexchange: since my answer answers your new version and your old version, but your old version has some unique aspects, I rolled back your edit. – Martijn Pieters May 22 '15 at 13:45
  • For your point: "The name `lst` itself is never altered", In the inner function, I could do `lst = [1, 2, 3]`, it works – overexchange May 22 '15 at 13:53
  • @overexchange: yes, you can assign something to the name `lst` in the inner function. It is then a local *everywhere in that function*. Move that assignment to the end of the function and you'll get an `UnboundLocal` exception. – Martijn Pieters May 22 '15 at 13:55
  • you mean to say, if expr `balance = balance - amount` wouldn't exist, I would not get this `unboundlocal` error. – overexchange May 22 '15 at 14:01
  • @overexchange: exactly, because that is a binding operation. – Martijn Pieters May 22 '15 at 14:01
  • Now I understood the meaning of your statement: "Assignments to subscriptions (using `[...]`) are not binding operations" – overexchange May 22 '15 at 14:03