diegoaguilar
diegoaguilar

Reputation: 8376

Working with a global variable inside a python function

At this code snippet:

def taylorVazquez(fx,a,b,n,puntos):

    puntosX = linspace(a,b,num=puntos)
    aproxY = []
    puntosY = []    

    def toFloat():
        puntosX = [float(x) for x in puntosX]
        aproxY = [float(y) for y in aproxY]
        puntosY = [float(y) for y in puntosY]

I'm getting the error message:

UnboundLocalError: local variable 'puntosX' referenced before assignment

And so I know same would happen to the other two variables. What can I do it's the outer taylorVazquez's variables the ones I manipulate at inner function, in other words, I want the assignments to work for the outer scope too

Upvotes: 3

Views: 143

Answers (3)

Justin O Barber
Justin O Barber

Reputation: 11601

Whenever you assign a value to a variable in a given scope, the variable is assumed to be local to that scope. Depending on which Python you are using, you will either need to use nonlocal (Python 3) or pass the values in using parameters (Python 2).

Here is Python 2:

def toFloat(puntosX, aproxY, puntosY):  # now the names of the enclosing function can be passed to the toFloat function
    puntosX = [float(x) for x in puntosX]
    aproxY = [float(y) for y in aproxY]
    puntosY = [float(y) for y in puntosY]

In Python 3:

def toFloat():
    nonlocal puntosX, aproxY, puntosY  # the names now refer to the enclosing scope rather than the local scope
    puntosX = [float(x) for x in puntosX]
    aproxY = [float(y) for y in aproxY]
    puntosY = [float(y) for y in puntosY]

global will NOT work in this situation, since you are referencing the names of an enclosing function.

One more thing, you MAY be trying to assign new values to the names in the enclosing scope. Your present tactic will not work, since you are assigning new objects to these names within the innermost function. (List comprehensions create new lists.) If you need to keep the new values, you will need to (for example) return these values to the enclosing scope and reassign your original names to the new values. For example, in Python 2:

def taylorVazquez(fx,a,b,n,puntos):
    puntosX = linspace(a,b,num=puntos)
    aproxY = []
    puntosY = [] 

    def toFloat(puntosX, aproxY, puntosY):  # now the names of the enclosing function can be passed to the toFloat function
        puntosX = [float(x) for x in puntosX]
        aproxY = [float(y) for y in aproxY]
        puntosY = [float(y) for y in puntosY]
        return puntosX, aproxY, puntosY

    puntosX, aproxY, puntosY = toFloat(puntosX, aproxY, puntosY)  # now you can reassign these names to the new values

Unlike global, you cannot assign new values to these names and have them hold for the enclosing scope.

Upvotes: 2

Blckknght
Blckknght

Reputation: 104802

If you assign to a variable in a Python function, the interpreter assumes that it is a local variable. All other uses of that variable are also assumed to be local, even ones that come before the assignment. This is why you get an exception about local variable 'puntosX' referenced before assignment.

There are a few possible solutions.

One is to declare the variable to be global (or in Python 3, nonlocal if its a local variable in an enclosing function).

def taylorVazquez(fx,a,b,n,puntos):

    puntosX = linspace(a,b,num=puntos)
    aproxY = []
    puntosY = []    

    def toFloat():
        nonlocal puntosX, aproxY, puntosY # this fixes the UnboundLocalError

        puntosX = [float(x) for x in puntosX]
        aproxY = [float(y) for y in aproxY]
        puntosY = [float(y) for y in puntosY]

Another option is to mutate the variable in place, rather than reassigning it. For instance, you can use list.append and list.__setitem__ on a list without ever making a local reference to it.

def toFloat():
    for i, v in enumerate(puntosX):
        puntosX[i] = float(v) # this calls puntosX.__setitem__, mutating it in place

    # etc...

A last option is to unnest your functions and pass arguments and return values, rather than relying on the nested namespaces to make your values available. For instance:

def toFloat(lst):
    return [float(v) for v in lst]

# then later
puntosX = toFloat(puntosX)

Upvotes: 2

Slater Victoroff
Slater Victoroff

Reputation: 21934

You can access the variable just fine, the issue here is that in Python2 there's no way to reassign the variable. There's a PEP in for fixing this issue (PEP-227).

It was in fact addressed in Python 3, and you could label your variables as nonlocal, but that's not a huge comfort if you're still using Python 2.

You can work around it by doing something like this:

def f1():
    x = [1]
        def f2():
        x[0] = 2
    f2()
    print x[0]
f1()

There are also a number of other potential workarounds, but most of them will result in unexpected behavior.

Upvotes: 1

Related Questions