Reputation: 497
Consider the following code:
def square(x):
return x**2.
def cube(x):
return x**3.
def some_func():
foo['a'] = 1.
foo['b'] = 3.
if foo['b'] > foo['a']:
bar = square
else:
bar = cube
foo = dict()
bar = None
some_func()
print(foo['a'])
print(bar(2))
Two different variables are initialised in the beginning, foo
, and bar
. Then I run a function which doesn't take arguments or return anything, but amends these two variables.
The result of these two is the following:
1.0
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-7e5e5ac79215> in <module>
22 print(foo['a'])
23
---> 24 print(bar(2))
TypeError: 'NoneType' object is not callable
So, the dict is happily amended by some_func()
, but I cannot assign a function name. Why? How can I go around this?
Edit: I have tried giving bar
other values than None
, similar behaviour occurs.
Upvotes: 0
Views: 405
Reputation: 17323
In order to assign to the global bar
from some_func()
, you need to declare it global
, like so:
def some_func():
foo['a'] = 1.
foo['b'] = 3.
global bar
if foo['b'] > foo['a']:
bar = square
else:
bar = cube
See the documentation for global, specifically this section:
The global statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals. While using global names is automatic if they are not defined in the local scope, assigning to global names would be impossible without global.
Your program clearly demonstrates the difference specified here: your access to foo
is using the global name, while your access to bar
is assigning to the global name.
Upvotes: 1
Reputation: 1850
Inside of some_func() you are adding key-value pairs to the dictionary object referenced by foo
. You are editing the object directly. bar
is referencing a None
object. When you attempt to re-assign the value of the label bar
inside of some_func()
it does so, but only for the scope of the function it is in. When it leaves the function whatever the bar
variable had reference to, None
in this case, is what it still references.
The difference is that you are manipulating the object foo
points to, but completely changing which object that bar
points to. If you could do anything with a None
object you would be able to manipulate it inside of some_func()
too.
Some here have offered the global
option, but unless you are really sure, that is probably not what you want. To truly assign a new value to bar
you should return it as a result of some_func()
. Also, you may consider passing in foo
as a parameter into some_func()
so that it is clear what variable you are referencing.
def square(x):
return x**2.
def cube(x):
return x**3.
def some_func(foo):
foo['a'] = 1.
foo['b'] = 3.
if foo['b'] > foo['a']:
bar = square
else:
bar = cube
return bar
foo = dict()
bar = some_func(foo)
Upvotes: 2
Reputation: 484
This has to do with the scope in python. You can read more about it in this article: https://www.datacamp.com/community/tutorials/scope-of-variables-python.
The given solution for your code would be to add: global bar
inside the function before the if statements to point it to the global variable that you are trying to set.
Upvotes: 1
Reputation: 7167
Try with a global statement for the thing you need to replace (and not just modify):
#!/usr/bin/python3
def square(x):
return x**2.
def cube(x):
return x**3.
def some_func():
global bar
foo['a'] = 1.
foo['b'] = 3.
if foo['b'] > foo['a']:
bar = square
else:
bar = cube
foo = dict()
bar = None
some_func()
print(foo['a'])
print(bar(2))
HTH
Upvotes: 1