-3

In Python, I have an expensive function a(x) which is currently evaluated multiple times within another function. At the start of that function, I want to evaluate a(x) once, and reassign the variable name a locally to that value. But I keep getting an Unbound Local Error.

As a MWE:

def a(x):
    return x+1

def main(x):
    a = a(x)
    return a**2

main(3)

#---------------------------------------------------------------------------
#UnboundLocalError                         Traceback (most recent call last)
#Input In [62], in <cell line: 8>()
#      5     a = a(x)
#      6     return a**2
#----> 8 main(3)
#
#Input In [62], in main(x)
#      4 def main(x):
#----> 5     a = a(x)
#      6     return a**2

#UnboundLocalError: local variable 'a' referenced before assignment

Obviously, a workaround is to use a line like b = a(x) instead.

But why is this happening? And how can I reassign the variable name a?

David
  • 201
  • 2
  • 10
  • 2
    What are you trying to achieve? Just name it `b`. – Peter Wood Aug 20 '22 at 14:43
  • 1
    Obviously, but I don't understand why the error arises. Hence, my question. – David Aug 20 '22 at 14:43
  • The function definition `a` is essentially a `global` name in the module. To assign to it you need to declare it `global` within the scope of the function (in this case `main`). – Peter Wood Aug 20 '22 at 14:45
  • However, this won't mean you can use the variable instead of the function as you use the function call syntax `a()`. Why not call `a()` and then pass the result to the function (`main` in this case)? – Peter Wood Aug 20 '22 at 14:47
  • If I understand correctly, you're saying that I cannot define a local variable `a`, because a global variable `a` is already defined. In order to change the variable definition of `a`, I need to change it globally, and cannot change it locally. – David Aug 20 '22 at 14:50
  • I do not understand your other suggestion. Calling `a(x)` is something I do already in the main function. When I try to assign it to a local variable `a`, that's where issues arise. – David Aug 20 '22 at 14:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/247413/discussion-between-david-and-peter-wood). – David Aug 20 '22 at 14:54
  • I think that should help: [`UnboundLocalError` on local variable when reassigned after first use](https://stackoverflow.com/questions/370357/unboundlocalerror-on-local-variable-when-reassigned-after-first-use/370363#370363) – Nikolaj Š. Aug 20 '22 at 14:58

3 Answers3

2

The error happens because in this line:

    a = a(x)

you are redefining a to be a local variable. This occurs for all uses for a within that scope, including the right hand side of the expression. a doesn't start off as a global and then become a local at some point during the function's execution at runtime; if there is an assignment to a anywhere in the function, then a is now a local everywhere in that function.

That means that when that line is actually executed, it's attempting to call the local variable a before anything has been assigned to it.

Here's a simpler demonstration of the same effect, where we aren't changing the type of a, and we aren't referencing it on the same line where we're assigning to it:

>>> a = 42
>>> def foo():
...     print(a)  # should be 42, right?
...     if False:
...         a = 42  # should be a no-op, right?
...
>>> foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'a' referenced before assignment

In this case, the rebinding of a happens after it's used, and it happens in a block that will never even actually be executed when the function is called -- but it doesn't matter, because at the time the function is defined, the existence of that assignment makes a a local variable.

You can actually see this by inspecting foo itself, without calling it:

>>> foo.__code__.co_varnames
('a',)

Compare with an implementation that doesn't make a into a local:

>>> def foo():
...     print(a)
...
>>> foo()
42
>>> foo.__code__.co_varnames
()
Samwise
  • 68,105
  • 3
  • 30
  • 44
0

The error is occurring because you have already named 'a' as a function that you can't assign as a variable again instead of

a = a(x)

you can name it some other variable like 'b' like this

def main(x):
    b = a(x)
    return b**2
Vatsal
  • 16
  • 4
  • Yes, but *why* can I not redefine `a` locally? – David Aug 20 '22 at 14:54
  • This example might be useful: https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value. Note, the problem in the non-working code example given there isn't per se that `x` is reassigned as local. It's that the global `x` then can't be used *anywhere in the function, even before the reassigment of x*, so the print statement fails. – slothrop Aug 20 '22 at 14:58
  • because Python recognizes it as a function that's why – Vatsal Aug 20 '22 at 15:06
  • 1
    @Vatsal but `def main(x): a = 42; return a * x` would be ok. The `main` function is allowed to redefine `a` as local, but if it does that then it can't *also* use the global `a`, as it does in `a = a(x)`. – slothrop Aug 20 '22 at 15:08
0

Try declaring a as global variable:

def a(x):
    return x+1

def main(x):
    global a  # <<<<<<<< only this line need to be added.
    a = a(x)  # Now a is first called (right side) and then reassigned
    return a**2

print(main(3))

Now it works!

Output:

16

If you check what is a now:

print(a)

Output:

4

So global a has changed. Its no more a function and it cannot be called now:

print(a(3))

Output:

TypeError: 'int' object is not callable
rnso
  • 23,686
  • 25
  • 112
  • 234