Reading Until EOF

  • Thread starter Scott Brady Drummonds
  • Start date
S

Scott Brady Drummonds

Hi, everyone,

I'm a Python novice and would love some tips on how I should perform the
following task: I'd like to have my Python script read objects (using their
constructors) and terminate gracefully when an EOF is encountered. My first
attempt looked like this:

# This is enclosed in a 'try' block
file = open(...)
while 1:
# The Object constructor raises an exception for end-of-file
object = Object(file)
self.list = self.list + [object]

However, I'm not entirely comfortable having the Object constructor raise an
exception when the end-of-file is hit. Per my experience with C++, this
does not qualify as "exceptional behavior". Frankly, it is accepted. I'd
like to save the exceptions for true exceptions. My next guess:

# This is enclosed in a 'try' block
file = open(...)
while object = Object(file):
self.list = self.list + [object]

With this implementation, the file reading stops both with an exception
(raised by a parsing error, as an example) and when the Object constructor
returns something that makes the while conditional fail. However, therein
lies the rub: I don't know how to do this in Python.

Is there a way for me to make the second implementation work? Is there an
"empty" operator in Python that I could overload? Can I have a constructor
return 'null'?

Thanks!
Scott
 
K

Kirk Strauser

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Scott Brady Drummonds said:
Per my experience with C++, this does not qualify as "exceptional
behavior". Frankly, it is accepted. I'd like to save the exceptions for
true exceptions. My next guess:

But it *is* exceptional behavior for the file read to fail. The normal
behavior is for the call to succeed, right?

Exceptions are handy for all sorts of things. One of my projects uses
something like:

class Success(Exception):
def __init__(self, added = 0, deleted = 0, modified = 0):
self.added = added
self.deleted = deleted
self.modified = modified

to hold return values from various calls. I use it like:

try:
SomeDatabaseQuery()
except Success, result:
print 'Just added %d new entries.' % result.added
except:
print 'The query failed.'

Don't limit yourself to a particular style simply because of the name.
- --
Kirk Strauser
The Strauser Group
Open. Solutions. Simple.
http://www.strausergroup.com/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (GNU/Linux)

iD8DBQE/lYgS5sRg+Y0CpvERAm9uAKCQsxlz2U7OgR/WI3FLSJKmACO0mwCePe9H
d9mWtYpgEYOoDe7XvYjBm4E=
=2ZUb
-----END PGP SIGNATURE-----
 
M

Michael Geary

Per my experience with C++, [end of file] does not qualify
as "exceptional behavior". Frankly, it is [expected]. I'd
like to save the exceptions for true exceptions.
But it *is* exceptional behavior for the file read to fail. The
normal behavior is for the call to succeed, right?

In C++, it would be considered bad style to use an exception to handle EOF.
The idea is that reaching EOF is something that you *expect* to happen, even
if only once. Similarly, the StopIteration exception would seem very odd to
a C++ programmer, since you expect to reach the end of an iteration
eventually.

You might use a C++ exception to handle something like a disk read
error--something that you do not expect to happen in the normal course of
events.

I realize the philosophy in Python is different--just trying to help clarify
why the OP is troubled by Python's more liberal use of exceptions. Anyone
coming from a C++ background is likely to have the same reaction at first.

-Mike
 
A

Alex Martelli

Scott Brady Drummonds wrote:
...
# This is enclosed in a 'try' block
file = open(...)
while object = Object(file):
self.list = self.list + [object]

With this implementation, the file reading stops both with an exception
(raised by a parsing error, as an example) and when the Object constructor
returns something that makes the while conditional fail. However, therein
lies the rub: I don't know how to do this in Python.

Is there a way for me to make the second implementation work? Is there an

It's a bad idea, as others have pointed out, but, sure -- as long as you
change this into something like:
while True:
object = Object(file)
if object is None: break
self.list.append(object)
or -- perhaps a bit more Pythonically, but otherwise equivalently:
def Objfromfile(): return Object(file)
self.list.extend([ object for object in iter(Objfromfile, None) ])
"empty" operator in Python that I could overload? Can I have a
constructor return 'null'?

I'm not sure what an "empty operator" would be. You can easily
make a function return None; use a factory function instead of the
bare ctor and you're all set (you'd do it that way in C++ too, and
there you'd have no alternative to that OR an exception).

You _CAN_ have Object.__new__ return what you want -- iff it
returns an instance of Object, that is then passed to Object.__init__.

Basically calling Object(xxx) is like calling a function that does:
def equivalent(xxx):
result = Object.__new__(Object, xxx)
if isinstance(result, Object): Object.__init__(result, xxx)
return result
but basically, for that to be used the way you want you need to do
almost all the work in Object.__new__ rather than in __init__ --
you can afford to let the latter be called only when you're fully
satisfied you'll be able to construct an Object instance just fine.

It IS a bad idea, but, if you insist, assuming that Object just
needs a field of exactly 4 bytes it reads from the file, period;
where you now have
def __init__(self, file):
self.field = file.read(4)
if len(self.field) != 4: raise EOFError
have no __init__ at all and instead a __new__:
def __new__(cls, file):
result = object.__new__(cls)
result.field = file.read(4)
if len(self.field) != 4: return None
return result

Using such exoteric techniques to avoid normal idiomatic usage
and surprise every Python-aware reader of your code IS a bad
idea. Be prepared to defend it craftily at the first code review!-)


Alex
 
R

Rainer Deyke

Scott said:
However, I'm not entirely comfortable having the Object constructor
raise an exception when the end-of-file is hit. Per my experience
with C++, this does not qualify as "exceptional behavior". Frankly,
it is accepted. I'd like to save the exceptions for true exceptions.

That's a common, but to my mind very strange preference. If you handle an
exception, the exception is by definition expected and "normal". Exceptions
are just a way to exit from nested loops and function calls; there is
nothing "exceptional" about them.
 
P

Peter Otten

Scott said:
I'm a Python novice and would love some tips on how I should perform the
following task: I'd like to have my Python script read objects (using
their
constructors) and terminate gracefully when an EOF is encountered. My
first attempt looked like this:

# This is enclosed in a 'try' block
file = open(...)
while 1:
# The Object constructor raises an exception for end-of-file
object = Object(file)
self.list = self.list + [object]

However, I'm not entirely comfortable having the Object constructor raise
an
exception when the end-of-file is hit. Per my experience with C++, this
does not qualify as "exceptional behavior". Frankly, it is accepted. I'd
like to save the exceptions for true exceptions. My next guess:

# This is enclosed in a 'try' block
file = open(...)
while object = Object(file):
self.list = self.list + [object]

With this implementation, the file reading stops both with an exception
(raised by a parsing error, as an example) and when the Object constructor
returns something that makes the while conditional fail. However, therein
lies the rub: I don't know how to do this in Python.

Is there a way for me to make the second implementation work? Is there an
"empty" operator in Python that I could overload? Can I have a
constructor return 'null'?

In Python constructors are precious, as there can only be one. I think it's
the wrong decision to let it construct an object from a file instance. I
would suggest a factory method instead:

class Object:
def __init__(self, word):
self.word = word
def __str__(self):
return self.word

def factory(instream):
""" construct an Object from the first word of each nonwhite line
"""
for line in instream:
if line.strip():
yield Object(line.split()[0].upper())

instream = file("fromfile.py")
try:
alist = [o for o in factory(instream)]
finally:
instream.close()

print "\n".join(map(str, alist))

The factory method could still explicitly raise an Exception for a more
complex file parsing algorithm (or simply return, if you stick with the
generator as shown above, but that is a StopIteration exception in
disguise), where you might want to separate the loop and the object
generation, but your client code is cleaner anyway.


Peter
 
B

Ben Finney

But it *is* exceptional behavior for the file read to fail. The
normal behavior is for the call to succeed, right?

No, it is a normal result for the call to succeed, and it is a normal
result for end of file to be reached. The existence of one normal
result does not make all others exceptional.
 
D

Donn Cave

Quoth Kirk Strauser <[email protected]>:
| At 2003-10-21T18:27:57Z, "Scott Brady Drummonds" <scott.b.drummonds.nospam@=
| intel.com> writes:
|
|> Per my experience with C++, this does not qualify as "exceptional
|> behavior". Frankly, it is accepted. I'd like to save the exceptions for
|> true exceptions. My next guess:
|
| But it *is* exceptional behavior for the file read to fail. The normal
| behavior is for the call to succeed, right?

Indeed, let me be the 7th or so person to chime in here. To be
precise, I think rather than `normal' and `succeed', I would just
say that the function has a meaning like `next line from file'.
Functions with such simple meanings can exist because of exceptions -
we decide what the function means, and we make it truly mean that by
raising an exception otherwise.

| Exceptions are handy for all sorts of things. One of my projects uses
| something like:
|
| class Success(Exception):
| def __init__(self, added = 0, deleted = 0, modified = 0):
| self.added = added
| self.deleted = deleted
| self.modified = modified
|
| to hold return values from various calls. I use it like:
|
| try:
| SomeDatabaseQuery()
| except Success, result:
| print 'Just added %d new entries.' % result.added
| except:
| print 'The query failed.'

Hm, it isn't so clear why this is a good idea.

Donn Cave, (e-mail address removed)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top