6

Let's say I've got the following functions:

def xplusy(x, y):
    return x+y

def xplus1(x):
    xplusy = xplusy(x, 1)
    return xplusy

Now if I call a = xplus1(4) it throws the following error:

UnboundLocalError: local variable 'xplusy' referenced before assignment

The error is because of the naming conflict, if I redefine xplus1 as follows:

def xplus1(x):
    s = xplusy(x, 1)
    return s

it works fine.

Why is it so: can't compiler properly distinguish between a variable and a function call?

Any ways around it?

10
  • 4
    Why to complicate your life? Just give it another name. Commented Apr 22, 2013 at 6:50
  • 1
    You can just do return xplusy(x,1), like this: def xplus1(x): return xplusy(x,1) Commented Apr 22, 2013 at 6:52
  • It is often a natural thing to do and extensively used in other languages. It is not a problem to change it, but if it is a question of adding a couple of characters to the code I'm fine with it. Why it happens theoretically is also unclear to me... Commented Apr 22, 2013 at 6:52
  • 6
    There is no difference between "variables" and functions in Python. Functions are objects like any other. Commented Apr 22, 2013 at 6:55
  • 3
    @sashkello you should also read up on pythons namespaces. Essentially namespaces are mappings from names to objects, thus any name can be overridden, including all builtin functions, anything you import to the local namespace and everything you define yourself. Commented Apr 22, 2013 at 7:08

5 Answers 5

18

In Python, functions are data, and typing is dynamic. This means that the following lines are valid Python:

def func(x):
    return x + 3

func = 3

func is now an int. The original function func is no longer referenced. The fact that func was originally a function doesn't have any bearing on what types of data can be assigned to it in the future. (This is what "dynamic typing" means.)

Therefore, since there's no static typing, and "function" is a valid data type, it wouldn't make sense for the Python interpreter to distinguish between a function and a piece of data referenced by the same name. Therefore, within a given scope, there's no way to use the same unqualified variable name to mean two different things.

In your particular case, if the code in your xplus1 function meant anything, it would mean "compute the value of xplusy(x,1) and assign that value to the variable xplusy -- thereby losing the reference to the function xplusy." However, within the scope of a function, the interpreter won't let you make an assignment to a variable outside of that scope, so it assumes that by writing an assignment statement, you're introducing a new local variable xplusy. The local variable, however, hasn't been defined yet, so your attempt to call it, xplusy(x,1), fails. The globally defined function is not called as a fall-back because, again, you can't have two unqualified names be identical and point to different data in the same scope.


Another example demonstrating the "no duplication of variable names within one scope" rule (which I actually only just discovered while playing around with the prompt in my attempt to construct this answer):

>>> def f1():
...     a = xplusy(3,4)
...     xplusy = 5
...     print xplusy
...
>>> f1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f1
UnboundLocalError: local variable 'xplusy' referenced before assignment
>>> def f1():
...     a = xplusy(3,4)
...     print a
...
>>> f1()
7

This demonstrates that it really is the scope, not the statement that requires unique names.


EDIT: This is a really cool post that explains this and other scoping-related behavior: http://me.veekun.com/blog/2011/04/24/gotcha-python-scoping-closures/

Sign up to request clarification or add additional context in comments.

3 Comments

Is this true with module names as well? I'm maintaining a python script that has a variable which has the same name as a module. (time, if you were wondering.)
@Jack I'm not sure what you're asking. It is true that import statements assign module names (which are not variable names) to variables, so they're unaffected by global or local variables that share the same name as a module; that is, time = 3; from time import time as epoch_time; print time; print epoch_time() will print 3 followed by the number of seconds since the epoch. However, time = 3; from time import time; print time will print <built-in function time>, since the variable time is reassigned by the import statement.
Minor correction: import statements can, of course, also assign members of modules to variables, not just modules themselves. Within the scope of the modules themselves, module members are, of course, variables. The real point is that import statements have two parts, a variable and a value. The variable name is in the current scope (since it's the target of the assignment being performed by the import statement), but the value, which is either a module or a module member, is not in a "scope" at all but rather depends on the module's existence in sys.path and its contents.
3

In python function is first class object which mean it is as any other object.

More information about What are “first class” objects?

1 Comment

Well, functions are also first-class objects in Common Lisp, but Common Lisp has a separate namespace for functions.
2

The reason this occurs is because xplusy exists in your scope as a global variable which cannot be changed unless you explicitly say xplusy is global.

def xplusy(x,y):
    return x+y

def xplus1(x):
    global xplusy
    xplusy = xplusy(x,1)
    return xplusy

however, this will make xplusy refer to an int, float or whatever xplusy returned the first time, meaning that after the first time it will throw TypeErrors.

The more pythonic way to do this would most likely be

def xplus1(x):
    return xplusy(x,1)

or using the functools module:

from functools import partial
xplus1 = partial(xplusy,y=1) #since you wanted to override y with 1

if you don't care about which argument is overridden, you could simply do

xplus1 = partial(xplusy,1)

Comments

1

There are cases, for example with callbacks, that you refer to functions by their static name (funcName instead of funcName()). Thus in Python that variable-name is reserved for the function. A lot like how you would use LAMBDA functions that you store in a variable.

Comments

1

In Python, xplusy can reference anything. You can do this:

def xplusy(x, y):
    return x+y

def otherfunction(x, y):
    return x*y

def xplus1(x):
    xplusy = otherfunction
    return  xplusy(x, 1)

And xplusy variable will reference to otherfunction in xplus1 scope.

xplus1(2)
>>> 2

Result is 2 * 1 instead 2 + 1. Be careful with assignments.Use as many variables as you need.

1 Comment

Thanks for this, looks quite weird :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.