IDLE mini-hack & ? (adding menu items, understanding *args, self)

Isidor rodisi01 at my-deja.com
Tue Jul 27 18:54:00 EDT 1999


Greg Ewing wrote:

> Isidor wrote:
> > 
> > ##def date_time_stamp(self):
> > def date_time_stamp():
> 
> I think the first version of that line was better.
> If it's a method of a class, it'll need a self
> argument. That would account for the "No arguments
> expected" exception - it's being called with one
> argument (self) but it's not expecting any.

Hello Greg - 

Thanks for responding to my query. I also thought that having "self" 
in there would help. I didn't think this because I knew what I was 
doing, but, because like a good monkey, I see that most funcs in a 
class have a "self" argument, so I figured, yup, better try it out. 
Unfortunately, it didn't work. In fact, it brought up an "odd" error: 


case 1: using: def date_time_stamp(self):  ...generates... 

>>> Exception in Tkinter callback
Traceback (innermost last):   
File "C:\Program Files\Python152\Lib\lib-tk\Tkinter.py", line 764, in 
__call__     
return apply(self.func, args) 
TypeError: too many arguments; expected 1, got 2 


Why do I call it an "odd" error message? Well, because to the 
untrained eye (e.g., mine) the previous error message is completely 
at odds with the following message: 

case 2: using   def date_time_stamp():  ...generates... 

>>> Exception in Tkinter callback
Traceback (innermost last):   
File "C:\Program Files\Python152\Lib\lib-tk\Tkinter.py", line 764, in 
__call__     
return apply(self.func, args) 
TypeError: no arguments expected 


To me it sounds as if in case 1, when I gave an argument, it said I 
gave too many while in case 2, when I didn't give an argument, it 
said it didn't want any. (Kind of like life?..;) 

But of course, what's wrong here is my totally ass-backwards 
interpretation of the error messages. I would never have figured this 
out if it hadn't been for an email sent to me personally by a kind 
gent who shall go unnamed here (he is being bcc'ed (hello and 
thanks!) (am i correct in assuming that a private emailer does not 
want public acknowledgment?)). He wrote: 

:::::::::::::::::::: 
You seem to have done everything right (I don't use TKInter enough to 
help with the real debugging), then ignored the information in the 
exception...  

Traceback (innermost last):   
File "C:\Program Files\Python152\Lib\lib-tk\Tkinter.py", line 764, in 
__call__     
return apply(self.func, args) 
TypeError: no arguments expected 

Which is trying to say: "Tkinter tried to call a function with too 
many arguments"  

So... 

def date_time_stamp( *args, **namedargs)
::::::::::::::::: 

Since at the time I understood nothing about the uses of *args, 
**namedargs , I did not understand his clue. Thanks to some recent 
posts and replies discussing these argument-passing-methods, I have a 
better grip on what's happening here. So, with a little time on my 
hands (yeah, right ;), I did a simple copy and paste from his 
message, and voila, it worked. I was able to pass through the 
function definition and into the interior of the function. He wasn't 
giving me a *clue*, he was giving me the freakin' *answer*! (Again, 
thanks.) 

error messages, a newbie's eye-view:

So now that it works, I am looking back, trying to reinterpret the 
error messages to fit with reality...not my ass-backwards version of 
them. 

case 1: def blah(self): 
error:  too many arguments; expected 1, got 2 

case 2: def blah(): 
error: no arguments expected 

It seems to me that the error message is being "spoken" by the 
"receiving" function (i.e., the "blah" func). In case 1, it is set up 
to handle one argument, but gets more than that and balks. In case 2, 
it is set up to handle no arguments, gets some, and balks. You see, I 
was thinking that the error was being "spoken" by something *after* 
the function had been defined (um, I have mentioned several times 
that I'm new at this, right?;)...so that whatever was being invoked 
after the def statement was balking in a very strange way...telling 
me that no arguments were expected when I didn't *give* it any 
arguments. Maybe this would have been clearer to me if the error 
message in case 2 were (similar to the error message in case 1): "too 
many arguments; expected 0, got 2", then I would have realized that 
both errors were variations on a theme rather than opposites. Of 
course, this new interpretation could be totally wrong too. If that 
is the case, I hope someone will point it out to me. 

One thing I don't really understand here is why it is necessary to 
raise an exception when an argument is passed to a function that 
isn't prepared to handle it. If the argument is needed *inside* the 
function, I could understand an exception being raised then (e.g., 
"hey, where's my argument"), but in my case i'm doing nothing (or at 
least I *think* I'm doing nothing) with those arguments. Why do I 
need to make a "landing strip" for them? To add more metaphors to the 
blender, something about this reminds me of the trick of putting a 
potato in a car exhaust pipe. If the exhaust has nowhere to go, it 
backs up until nothing can move in or out of cylinders and the motor 
dies (or at least it did in Beverly Hills Cop...or whatever that 
Eddie Murphy movie was). So, maybe by allowing the unused arguments 
to pass through my function, everything flows nicely and nothing gets 
stopped up. Hmm, strange. Am I on basically the right track here with 
this idea? 

Understanding the "self" ("the toughest thing of all" - stated in a 
philosophy of science seminar on reflexivity i once attended):

So, anyway, what did I have inside that function? Well, basically 
just some test code to see if I was getting that far:

print "cheese"

It worked, but poorly. It pushed whatever text was already on the 
line (at the command prompt) on to the next line and jumped to the 
front of the line. Hmm. I suspected that using "print" was like using 
a sledgehammer where a scalpel was needed. Scanning through the other 
functions in EditorWindow.py, I saw a lot of things being done to/by 
self.text. In the init constructor, I saw that "self.text = 
Text(...lots of arguments that would make sense for a text widget 
type thing...)", and I realized, aha, "text" must be the arena in 
which we are actually playing when using IDLE. So, then I wondered, 
what "methods" does "text" provide me to insert text? I looked at the 
Tkinter text man page and found a section on insert that looked 
reasonably like what I wanted to do, but it didn't look anything like 
what I was seeing in EditorWindow.py (to me, at least). So then I id 
a "Find in Files..." (oh my, what a wonderful feature!) for 
text.insert in the idle files and found a nice example in line 51 of 
Autoexpand.py. Something like:

self.text.insert("insert", newwords)

My guess is that insert is a method of text, and "insert" (first 
argument) is the location of the insertion point and will be the 
location where the second argument gets injected into "text". So, 
just for kicks, I copied that line into my newly-functioning def, 
changed "newwords" to "timestr" (which contained the date-time string 
I wanted), and tried it out. (I suspected it wasn't going to work, 
but I wanted to try anyway.)

case 3: 

def date_time_stamp( *args, **namedargs):
	timestr = ...stuff...
	self.text.insert("insert", timestr)

>>> Exception in Tkinter callback
Traceback (innermost last):
  File "C:\Program Files\Python152\Lib\lib-tk\Tkinter.py", line 764, 
in __call__
    return apply(self.func, args)
  File "C:\PROGRA~1\PYTHON15\TOOLS\IDLE\EditorWindow.py", line 586, 
in date_time_stamp
    self.text.insert("insert", timestr)
NameError: self

I was curious: I wanted to know whether a function inside a class 
could automatically see the methods and attributes of that class. 
Well, this error message makes me think that the answer is no. Self 
has to be "created" in the arguments of the function. Ok, so now I'm 
starting to understand *why* everyone always says you have to have 
"self" as the first argument. But, I wondered, maybe "self" doesn't 
work here because I haven't created it within the context of this 
func, but "text" would be recognized because it is created in the 
class that houses the func. So I tried the following (hey, give me a 
break, I'm a newbie! ;) :

case 4:
def date_time_stamp( *args, **namedargs):
	timestr = ...stuff...
	text.insert("insert", timestr)


>>> Exception in Tkinter callback
Traceback (innermost last):
  File "C:\Program Files\Python152\Lib\lib-tk\Tkinter.py", line 764, 
in __call__
    return apply(self.func, args)
  File "C:\PROGRA~1\PYTHON15\TOOLS\IDLE\EditorWindow.py", line 587, 
in date_time_stamp
    text.insert("insert", timestr)
NameError: text

Nope, as I suspected, that didn't work either. If I am correct, then, 
these results point to the interesting fact that a function knows 
nothing about its context that it isn't told, and that the first 
argument (conventionally "self") of an argument within a class is 
used by the class to pass itself and its context to the function, and 
is used by the func to "catch" everything the class has to offer. So 
I did what I should have done all along:

case 5:

def date_time_stamp(self, *args, **namedargs): 
	timestr = ...stuff...
	self.text.insert("insert", timestr) 

result upon selecting the menu/or hitting the right keys:

>>> 19990726.1735

Yay. It works. Just to see if I understood what was going on here, I 
tried the following (pushing my luck, I know!):

case 6:
def date_time_stamp(blah, *args, **namedargs): 
	timestr = ...stuff...
	blah.text.insert("insert", timestr) 

Yup, that worked too. So now I understand what they mean when they 
say that calling the first argument "self" is just a convention. 
*Having* the argument there isn't the convention, in fact, in most 
cases it's *necessary*, but it can be *called* almost anything. "Aha, 
the grasshopper is beginning to understand." ;^)

"The self is just an empty vessle into which...."
"The self must be empty before...."

	no no no no... ;) 


Ok, seriously now. Here is something I would like to know. Is it 
possible for function y in a class to have access to a variable 
defined in function x of that same class (without having to 
explicitly pass the variable with a return statement)?:

def class something:

1	...stuff, including __init__...

2	def funcx(self, *args, **namedargs):
3		zip = "googoo"
 
4	def funcy(self, *args, **namedargs):
5		print zip

Something causes me to suspect that this won't work. What would make 
it work? Can I attach zip to "self" somehow in funcx, e.g., line 3: 
'self.zip = "googoo"'?  If I then change line 5 to 'print self.zip', 
will this work? Will it only work if self.zip is defined in the 
__init__ func? Until I trust myself to create a working class, I 
don't think I can reliably test this out myself. In the meantime, I 
will welcome any insights anyone is willing to provide (including 
"read chap x, sect y of tutorial or language reference").

Ok, I hope this rambling troubleshooting log helps some other newbie 
understand what the hell is going on with passing arguments to 
functions, including class "context".

Thank to Greg and my friendly e-mailer for the helpful advice! With 
this post and my original one, it should be possible to make a "date- 
time stamp" (or any other "insert something here") menu item. If you 
can't find the original post and want to do this, email me, and I'll 
put it together more coherently for you. 

Thanks again, and take care. 

Isidor 




More information about the Python-list mailing list