I m trying to spread my code across separate files to improve readability, and I m running in to trouble with undefined global names being referenced inside imported files. Is there a better solution than deliberately passing all necessary values to the functions when called?
My code currently is like this:
#foo.py
import bar
def main():
a = 1
b = bar.Bar()
while True:
a += 1
b.incr()
print a
print b.c
if __name__ == __main__ :
main()
#END foo.py
-
#bar.py
class Bar:
def __init__(self):
self.c = 0
def incr(self):
self.c += a
#END bar.py
It gives me NameError: global name a is not defined. Do I need to rewrite main() like this:
def main():
b = new bar.Bar()
while True:
a += 1
b.incr(a)
print a
print b.c
And rewrite incr() like this:
def incr(self,a):
c += a
Or is there a better way?
For the record, the above code is heavily abstracted. The actual code involves a great many classes and functions passing many variables of several types, including some large dictionaries.
Thanks in advance!
If you just want to know why it s not working:
First, main isn t setting a global a, but a local one. If you want to make it global, you have to be explicit:
def main():
global a
a = 1
# ...
But this won t fix the problem, because "global" in Python is per-module. In other words, that just creates a foo.a, but your code in bar.Bar will be looking for bar.a, which will not exist.
You could import foo and then access foo.a if you wanted. (In this case, the circular dependency shouldn t be a problem, and the if __name__ == __main__ won t get executed twice.) Or you could assume that foo has been imported, and use sys.modules[ foo ].a. Or you could inject a into bar s dictionary from foo. Or lots of other tricks. But these are all horrible things to do.
If you can t make a global work, the right answer is almost always to not use a global. (In fact, even when you can make a global work, that s usually the right answer.)
So, how do you do that? Without knowing more about the details, it s hard to guess whether a belongs as an explicit module global, a class attribute, an instance attribute, a parameter to incr, etc. But figure out which one makes the most sense given what a represents, and do that.
In the comments, you suggested that you have a bunch of these configuration variables. If you wrap them all up in a dict and pass that dict to each object s constructor, that will make things simpler.
More importantly, a dict is a reference to a mutable object. Copying it just makes another reference to that same object.
main.py:
def main():
configs = {}
configs[ gravity ] = 1.0
rock = rps.Rock(configs)
rock.do_stuff()
configs[ gravity ] = 2.1
rock.do_stuff()
if __name__ == __main__ :
main()
rps.py:
class Rock(object):
def __init__(self, configs):
self.configs = configs
def do_stuff(self):
print( Falling with gravity {}. .format(self.configs[ gravity ]))
When you run this, it ll print something like:
Falling with gravity 1.0.
Falling with gravity 2.1.
You haven t modified the value configs[ gravity ] (that s an immutable float; you can t modify it), but you have replaced it with a different value (because configs is a mutable dict; you can modify it just fine). You can try printing out things like id(self.configs), id(self.configs[ gravity ]), etc. in various places if the identity isn t clear.
http://stackoverflow.com/questions/15397148/global-variables-in-imported-class-functions-in-python