Mindboggling Scope Issue
Steve Holden
steve at holdenweb.com
Sun Oct 24 20:23:48 EDT 2004
James Stroud wrote:
> Hello All,
>
> Please bare with me as I have seldom asked for help with my code, so I may do
> things a little wrong at times. If I ask in the wrong way, please by friendly
> like Tarry Reedy and suggest how I may better ask my questions.
>
> So I will retry this one from the top, and with Tarry's suggestions
> incorporated.
>
That's great!
> First I'll begin with a simple example that works from the python CLI:
>
> % python
> Python 2.3.3 (#2, Feb 17 2004, 11:45:40)
> [GCC 3.3.2 (Mandrake Linux 10.0 3.3.2-6mdk)] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
>
>>>>def outer():
>
> ... def inner():
> ... print bob
> ... bob = "wuzzup?"
> ... inner()
> ...
>
>>>>outer()
>
> wuzzup?
>
> This is an experiment to prove that a function can "de-reference" a name even
> if the function is defined in a position of the code that preceeds said
> name's assignment in the enclosing scope. (Hopefully my vocabulary makes
> sense here--I am not a computer scientist). Please look at the code to see
> what I mean. Here, we are talking about the name "bob".
>
Yes, variable references in the function definition are only
de-referenced when the function is executed. As long as bob has a value
when inner() is called everything is OK.
Having said that, using global variables (or any variable in an outer
scope) intoduces couplings in your code which make it much less easy to
maintain. It's sometimes unavoidable, but the ideal is to have all
inputs as function parameters and all results returned byt he function.
> Second I present below two alternatives (designated "method1" and "method2" in
> the comments of the listings below) to a method that resides in a program I
> am writing. I run the program with the same python executable with which I
> generated the above example. These method listings are long but differ only
> where I have indicated in the comments. I am including them in their entirety
> so that I don't leave something critical out that I, as a relative novice, am
> missing.
>
> With "method1" I get no error and the "ok()" method works as I expect and in
> accord with the example above. With "method2" I call "ok()", and when it
> prints to stdout I get the following error (after compile, when the program
> is being run):
>
> Exception in Tkinter callback
> Traceback (most recent call last):
> File "/usr/lib/python2.3/lib-tk/Tkinter.py", line 1345, in __call__
> return self.func(*args)
> File "./passerby", line 1378, in ok
> print result
> UnboundLocalError: local variable 'result' referenced before assignment
>
Excellent. Because the PassWindow object is (presumably) defined in the
top-level scope of the module it can be referenced from inside the
nested function definitions as well as the outer function definitions.
Because what's being referenced is an attribute of a globally-available
object, everything works.
The problem arises with the "result" version because the inner functions
contain assignments to the "result" variable. The compiler therefore
assumes that they are local.
> So my question is, what is special about what I am doing here? Why does it not
> work like the first example I gave with the name bob? Have I found a bug in
> python? I should note that "passWindow" is not defined anywhere else but this
> method. Also, I should note that "method1" represents a
> workaround/alternative, so my program runs just fine. I just want to know
> what is going on here to imporve my own understanding. I think if anyone can
> give me a satisfactory answer on this one, we should all call them a "real
> expert".
>
> Here are the listings I referred to above:
>
> -----
>
> # method1 : works!
> def ask_for_password(self, message):
> def cancel():
> passWindow.destroy()
> def ok():
> # different : WORKS FINE!
> print passWindow.result
> pw = passEntry.get()
> if len(pw):
> # different
> passWindow.result = pw
> passWindow.destroy()
> message = "\n" + message + "\n"
> passWindow = Toplevel(self.get_mainWindow())
> # different : notice that passWindow is defined for the
> # first time in this whole program in the line above
> # It is not part of a larger scope!
> passWindow.result = None
> passWindow.title("passerby - Password Entry")
> self.get_mainWindow().deiconify()
> passWindow.transient(self.get_mainWindow())
> passWindow.geometry("400x180+50+50")
> passWindow.resizable(0,0)
> passLabel = Label(passWindow, text=message)
> passEntry = Entry(passWindow)
> passEntry.configure(width=48)
> passEntry.config(show="*")
> passEntry.bind("<Return>",ok)
> cancelButton = Button(passWindow, text="Cancel", command=cancel)
> okButton = Button(passWindow, text="OK", command=ok)
> passLabel.pack()
> passEntry.pack()
> cancelButton.pack()
> okButton.pack()
> passEntry.focus_set()
> passWindow.grab_set()
> self.get_tk().wait_window(passWindow)
> # different
> return passWindow.result
>
> -----
>
> # method2 : doesn't work!
> def ask_for_password(self, message):
> def cancel():
> passWindow.destroy()
> def ok():
> # different : GETS ERROR!
> print result
> pw = passEntry.get()
> if len(pw):
> passWindow.result = pw
> passWindow.destroy()
> message = "\n" + message + "\n"
> passWindow = Toplevel(self.get_mainWindow())
> # different : notice that passWindow is defined for the
> # first time in this whole program in the line above
> # It is not part of a larger scope!
> result = None
> passWindow.title("passerby - Password Entry")
> self.get_mainWindow().deiconify()
> passWindow.transient(self.get_mainWindow())
> passWindow.geometry("400x180+50+50")
> passWindow.resizable(0,0)
> passLabel = Label(passWindow, text=message)
> passEntry = Entry(passWindow)
> passEntry.configure(width=48)
> passEntry.config(show="*")
> passEntry.bind("<Return>",ok)
> cancelButton = Button(passWindow, text="Cancel", command=cancel)
> okButton = Button(passWindow, text="OK", command=ok)
> passLabel.pack()
> passEntry.pack()
> cancelButton.pack()
> okButton.pack()
> passEntry.focus_set()
> passWindow.grab_set()
> self.get_tk().wait_window(passWindow)
> # different
> return result
>
>
>
The structure of your code could perhaps be better-ordered. I don't know
whether you've seen much other Tkinter code, but there are fairly
well-defined guidelines for how to proceed, and they certainly helped me.
You might want to take a look at some of the following links to help you
to improve your coding skills:
http://www.pythonware.com/library/tkinter/introduction/index.htm
(by Fredrik Lundh, a well-known Python programmer who has contributed
extensively to the Tkinter implementation).
http://www.manning.com/catalog/view.php?book=grayson&item=source
(Code samples from the book "Python and Tkinter Programming")
regards
Steve
--
http://www.holdenweb.com
http://pydish.holdenweb.com
Holden Web LLC +1 800 494 3119
More information about the Python-list
mailing list