Here's an example of what I mean:
def foo():
foo = 5
print(foo + 5)
foo()
# => 10
The code doesn't produce any errors and runs perfectly. This contradicts the idea that variables and functions shouldn't have the same name unless you overwrite them. Why does it work? And when applied to real code, should I use different function/local variable names, or is this perfectly fine?
foo = 5
creates a local variable inside your function. def foo
creates a global variable. That's why they can both have the same name.
If you refer to foo
inside your foo()
function, you're referring to the local variable. If you refer to foo
outside that function, you're referring to the global variable.
Since it evidently causes confusion for people trying to follow the code, you probably shouldn't do this.
The answer is yes.
Functions are first-class objects in Python. There is no fundamental difference between the foo
function and the foo
variable. Both of those are references to the memory and both have a scope, so they are both equivalent to a variable.
If you have defined foo as a function and then don't overwrite it locally, it will be like a global variable (taken from the upper-level scope):
def foo():
print(foo)
and then if you overwrite it with a local variable, it will just define a local variable within the function scope:
def foo():
foo = 3
print(foo)
In Python, every reference (variable) can be overwritten for the life span of a particular scope. You can try:
def foo(): pass
foo = 3
This will overwrite the value of the foo and it will point now to 3 instead of the function foo in the memory.
Well, it is because of the scope of variable each foo
is. The function foo()
is in the global scope, therefore can be called within other functions and outside of functions.
The variable foo
however, is in the local scope. That means it only "exists" inside of the function, it cannot be called or referenced outside of the function.
So, each different function creates its own local scope when it is called, and the variables created within are forgotten as soon as the scope is destroyed (the function ends).
Global variables can be accessed within local scope, local variables cannot be accessed in global scope.
If you want to create the variable in the global scope, call it like this:
global var
Here is an example of global and local scope:
var=1
def foo1():
var=3
foo1()
print(var) #prints 1 because the "var" in foo1() is locally assigned
def foo2():
global var
var=2
foo2()
print(var) #prints 2 because "var" is global
So, your function works because it i only assigning the name foo
locally, not globally.
However, if you call foo()
later in that function, it will raise an error because that local scope has assigned an int value to foo
, not a function, therefore is not callable.
Something nobody else has mentioned yet: Python is a dynamic language, with very little static checking. So when you write
def foo():
foo = 5
print(foo + 5)
you might have been thinking of that as a "contradiction" — how can foo
be a function and an integer variable at the same time? The first-order answer is "It's two different foo
s," because the inner foo
is just a local variable, unrelated to the global name foo
. But make it global, and the code still works!
def foo():
global foo
foo = 5
print(foo + 5)
foo() # OK, prints 10
In this code there is only one foo
! However, it is not "both a variable and a function." First, on lines 1–4, we define foo
as a function with a certain body — but we do not execute that body yet. After line 4, the global foo
holds a function. Then, on line 6, we actually call the function referred to by foo
, which executes its body. The first thing the body does is assign 5
to foo
... and now foo
holds an integer. By running code that assigns a new value to foo
, we've changed foo
's value. It used to be that-function-there; now it's 5. In a dynamically typed language like Python, there's nothing wrong with this.
def foo():
global foo
foo = 5
print(foo + 5)
foo() # OK, prints 10 (and incidentally assigns a new value to foo)
foo() # Raises TypeError: 'int' object is not callable
Another problem with using the same name is obviously the ambiguity when writing recursions:
def fibo(n: int) -> int:
if n <= 1:
fibo = 1
else:
fibo = fibo(n - 1) + fibo(n - 2)
return fibo
Results in:
UnboundLocalError: local variable 'fibo' referenced before assignment
It happens because when fibo
is defined inside the function.
When Python tries to run fibo(n - 1)
it searches the local fibo
variable, and fails to find its value.
Just a short remark: pylint marks this with a warning. pylint is our friend!