0

I come from a land of heavy Java and PHP experience so when it comes to Python much of the rules do not make sense to me.

I have a recursive Fibonacci function below that spits out the error:

Traceback (most recent call last):
  File "C:\Users\Nic\workspace\lab8\src\Main.py", line 26, in <module>
    print fibcount(27),"took",calls,"calls."
  File "C:\Users\Nic\workspace\lab8\src\Main.py", line 19, in fibcount
    calls += 1
UnboundLocalError: local variable 'calls' referenced before assignment

Here is my code:

calls = 0

def fibcount(n):
    calls += 1
    if n < 2:
        return (1,1)
    f1,c1 = fibcount(n-1)
    f2,c2 = fibcount(n-2)
    return (f1+f2,c1+c2+1)

print fibcount(27),"took",calls,"calls."

In Java this would obviously work because calls is a global variable in respect to the function fibcount() so it confuses me that calls is somehow not in scope.

What am I doing wrong?

Nic
  • 37
  • 1
  • 2
  • 4

1 Answers1

8

In Python, you need to declare global variables as being global inside functions which reassign them to a different value. You would use global for this:

def fibcount(n):
    global calls
    ...

Otherwise, Python will treat the calls variable as if it were local.

Note however that you only need to do this for global variables which you reassign. Simply reading their values will work fine.

Also, reassigning global variables like this is considered ugly in Python and should be avoided whenever possible. Most of the time, you should stick to passing values to functions and then reassigning variables to their return values. If you need to maintain state, use a class.

The only time global variables are warranted in Python is when you want to have module-level constants. And in that case, the convention is that they should have all-caps names:

MYCONST = 12345
  • I think it was good to explicitly note that the `global` keyword is not needed when you are only reading the value. However, I think we need to be careful with the word "modify", since folks coming from many other languages don't have a strong mental distinction between rebinding and mutation (and would consider both things "modification"). We only need `global` if we are *rebinding* (a.k.a. *reassigning*) the name. We can freely mutate global objects (for example, if `calls` were a list, we could append or delete its elements without the `global` keyword). – John Y Dec 05 '14 at 17:57
  • There, I changed "modify" to "reassign". I was trying to avoid a discussion of how "Python doesn't have variables. It has names" since that might confuse the OP. The important part here is to use the `global` keyword. The exact details of why are beyond the scope of my answer. :) –  Dec 05 '14 at 18:03
  • I fully appreciate not wanting to open that can of worms! To that end, I think using as precise terminology as possible up front gives the can its best chance to stay closed. I understand that sometimes you can't explain something to someone without taking liberties with the terminology until they have more understanding, but in this case, *reassign* is a good choice: It improves precision without increasing verbosity. – John Y Dec 05 '14 at 18:19