[Tutor] Line in a file

alan.gauld@bt.com alan.gauld@bt.com
Fri, 19 Jul 2002 17:37:59 +0100


> My first question about the above code, is why couldn't I put an 
> exception handler in there with the finally?  I thought that 
> you could do this

Nope you have to nest them:

try:
  try:
    #do it here
  except: # catch errors
finally: #tidy up

Which try goes inside is a fairly arbitrary choice, I prefer 
the finally outside because it then comes finally in the code too...

> So then I tried it this way:
> 
>  >>> def printline(fh, num):
> ...  c = 0
> ...  try:
> ...   for line in fh.xreadlines():
> ...     c = c + 1
> ...     if (c == num):
> ...       return line
> ...   raise IndexError
> ...  except IndexError:
> ...   print "The line you want was not found.\n"

The problem is you are raising and catching the exception, 
you only want to raise it so that your user can catch it!

remove the try/except and keep the raise.
(sounds like a poker game!)

then wrap the call to your function in a try/except:

>>> def printline(fh, num):
...   c = 0
...   for line in fh.xreadlines():
...     c = c + 1
...     if (c == num):
...       return line
...   raise IndexError
...
>>> try: printline(f,27)
... except IndexError: print "Couldn't access line 27"
...

Of course you also should define the IndexError 
exception class too:

class IndexError(Exception): pass

> it seems like I'm not using proper style.

You are trying to handle your own exception rather 
than raising it for your user to catch.

> Anyways, with this version, there's no "finally", so I can't 
> reset the file pointer with fh.seek(0).  

But you can now we;'ve removed the try/catch
>>> class IndexError(Exception): pass
>>> def printline(fh, num):
...   c = 0
...   try:
...     for line in fh.xreadlines():
...       c = c + 1
...       if (c == num):
...         return line
...    finally: fh.seek(0)
...    raise IndexError()

> I liked it better the first time. 

Is that any better?

> By doing it this way, 

Exactly right. I should have read further before 
'explaining'!


> It seems like I should be able to just have the 
> exception handler defined within the function definition

No, you want a way to signal to the function user that 
an error occured, that's exactly what raise is for. It 
only makes sense for you to handle the exception if you 
know how to recover from it. Given that you don't know 
how the client wants to deal with a non existent line 
you have to pass responsibility back to them!

> want to use "finally" to reset my file pointer.  Is this 
> really the way it's supposed to work?

Absolutely. The finally is something that you as function 
writer want to guaranrtee about the function - namely that 
it returns the line requested and leaves the file pointer 
at 0.

> Extra credit: can someone give me a clue as to how to make my 
> "printline()" function a method of a file pointer, so 
> that instead of

You'd need to create your own file subclass:

class RandomFile:
   def __init__(fn,md='r'):
     self.fp = open(fn,md)
   def printLine(num): # code here
   # and other file methods

Now you can open your class:

try:
  fp = RandomFile('foo.txt','r')
  fp.printline(27)
except: print 'oops!'

> That seems like the way it should be done.  I seems like I'll have to 
> extend the file pointer class.  But not sure...

Yes, if thats an extensible class, or there might be a 
UserFile or somesuch. But thats what you'll need to do.

Alan G.