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?

sashkello
  • 17,306
  • 24
  • 81
  • 109
  • 3
    Why to complicate your life? Just give it another name. – Maroun Apr 22 '13 at 06:50
  • 1
    You can just do `return xplusy(x,1)`, like this: `def xplus1(x): return xplusy(x,1)` – Burhan Khalid Apr 22 '13 at 06: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... – sashkello Apr 22 '13 at 06:52
  • And yes, I do know how to write this without any errors. If I paste my huge code here everyone asks to simplify it, but if I simplify it, everyone tells me it is obvious. This is a general question, guys, not an actual code I'm writing :) – sashkello Apr 22 '13 at 06:54
  • Side-note: Python isn't compiled, typically. You probably mean "can't the compiler properly distinguish..." – Kyle Strand Apr 22 '13 at 06:55
  • 6
    There is no difference between "variables" and functions in Python. Functions are objects like any other. – BrenBarn Apr 22 '13 at 06:55
  • Why downvotes? Explain if you are doing it. Is it a duplicate? Then just post a link. – sashkello Apr 22 '13 at 06:57
  • 3
    @sashkello you should also read up on pythons [namespaces](http://docs.python.org/2/tutorial/classes.html#python-scopes-and-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. – msvalkon Apr 22 '13 at 07:08
  • Thanks all! Interesting stuff and useful links... Your help is greatly appreciated. – sashkello Apr 22 '13 at 07:09
  • @sashkello: I'm not sure why people downvoted. Good thing upvotes count for more, eh? – Kyle Strand Apr 22 '13 at 07:37

5 Answers5

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/

Kyle Strand
  • 15,941
  • 8
  • 72
  • 167
  • 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.) – Jacklynn Jul 10 '14 at 16:05
  • 1
    @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 ``, since the variable `time` is reassigned by the import statement. – Kyle Strand Jul 10 '14 at 17:42
  • 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. – Kyle Strand Jul 10 '14 at 17:48
3

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

More information about What are “first class” objects?

Community
  • 1
  • 1
emcpow2
  • 852
  • 6
  • 19
  • 4
    Well, functions are also first-class objects in Common Lisp, but Common Lisp has a separate namespace for functions. – Dietrich Epp Apr 22 '13 at 07:02
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)
Snakes and Coffee
  • 8,747
  • 4
  • 40
  • 60
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.

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.

emigue
  • 492
  • 3
  • 13