namespaces, scoping and variables

Ethan Furman ethan at stoneleaf.us
Mon Aug 2 16:04:01 EDT 2010


Chris Hare wrote:
> I am having a problem getting around this variable namespace thing.
> 
> Consider these code bits
> 
> File a.py
> from Tkinter import *
> import a1
> 
> def doAgain():
> 	x = a1.Net()
> 	x.show("Again!")
> 
> root = Tk()
> root.title("test")
> f = Frame(root,bg="Yellow")
> l = Button(root,text="window 1",command=doAgain)
> f.grid()
> l.grid()
> a = 5
> x = a1.Net()
> x.show("window 2")
> if __name__ == "__main__":
> 	root.mainloop()
> 
> File a1.py
> from Tkinter import *
> 
> class Net:
> 	def __init__(self):
> 		self.window = Toplevel()
> 	def show(self,t):		
> 		self.l = Label(self.window,text=t)
> 		self.l.grid()
>                 button = Button(self.window, text="Again")
> 		button.bind("<Button-1>", self.Again)
>                 button2 = Button(self.window, text="Dismiss")
> 		button2.bind("<Button-1>", self.hide)
> 		button.grid()
> 		button2.grid()
> 	def Again(self,event):
> 		x = Net()
> 		x.show(a)
> 	def hide(self,event):
> 		self.window.destroy()
> 
> 
> When I run a.py, it imports a1.py and click on the Again button, I get the error
> 
> Exception in Tkinter callback
> Traceback (most recent call last):
>   File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk/Tkinter.py", line 1410, in __call__
>     return self.func(*args)
>   File "/Volumes/Development/py/a1.py", line 17, in Again
>     x.show(a)
> NameError: global name 'a' is not defined
> 
> I believe this is the expected behavior.  so my question is this -- how do I tell the code in a1.py about the variable a, which exists in a.py?  Do I have to pass it as part of the function call, or what?  using
> 
> global a
> 
> in a1.py doesn't change anything.

The global keyword does not make a variable global.  It tells the 
interpreter that the variable in question can be find in the module 
scope, not the function/method scope.  In other words, the variable is 
global to the module, but not to the whole program.

What you'll need to do is pass a into Net when you instanciate it, like 
so (untested):

   def doAgain():
       x = a1.Net(a)
       x.show("Again!")

and in Net:

class Net:
     def __init__(self, some_number):
         self.some_number = some_number
         self.window = Toplevel()
     .
     .
     .
     def Again(self,event):
         x = Net(self.some_number)
         x.show()

Keep in mind, though, that if you change a in a.py after you've 
instanciated Net, your Net instance will not see the change.  For the 
change to show up, a would need to be mutable, and you would have to 
mutate it.  The other option is to change the Net instance's some_number 
directly (i.e. x.some_number = 9).

Hope this helps.

~Ethan~



More information about the Python-list mailing list