Yet ANOTHER of Steve's File Objects ( lazy readlines methods )

Steven D. Majewski (sdm7g@elvis.med.Virginia.EDU)
Thu, 31 Mar 1994 21:46:46 GMT

OK - I know you folks on the mailing list probably think you've
seen enough of these, but THIS ONE IS REALLY DIFFERENT - I PROMISE!
I think I've _finally_ got it Right!

( And maybe someone on the news group hasn't seen one yet! )

Program first, then output.

- Steve Majewski

#!/usr/local/bin/python
#
# Yet ANOTHER of Steve's File Objects ( lazy readlines methods )
#
# Here is a different way of creating a Class wrapper around
# builtin fileobjects and other builtin objects.
# Most of the methods go into the Object, rather that the Class
# This is almost sort of like a dynamic binding of a parent
# class at initialization time, except that the parent isn't
# necessarily a class. There are wider implications of this.
# More on that later.
#
# This class is dedicated to Tommy Burnett - whose comments
# about whether Python is really Class based inspired this method.
#
#
# ( It was either our conversation, or seeing double wearing that
# crazy VR helmet. I went home and coded this up after dinner.
# Maybe I should have named it Tommy() ? - Well, nobody seemed
# to like Spam-tables ... :-)
#
# - Steve Majewski <sdm7g@Virginia.EDU>
#

class File:
def __str__( self ):
return repr( self._obj )
def __repr__( self ):
return repr( self._obj )
def __init__( self, obj ):
# Note that self.__methods__ is a READ ONLY attribute,
# EVEN IF IT DOES NOT EXIST !!!
# so we have to use _methods_ instead.
if hasattr( obj, '__methods__' ):
self._methods_ = obj.__methods__[:]
elif hasattr( obj, '_methods_' ):
self._methods_ = obj._methods_
self._obj = obj
for meth in self._methods_ :
setattr( self, meth, getattr( obj, meth ))
#
# OK - I've poster several similar classes, but this is (finally)
# a generic way of creating them. The turns an object with a
# method for getting the next item ( like file.readline ) into
# a deferred evaluation, read-only stream object that can be
# sequenced in a for loop: "for each in Stream( file.readline ):"
#
# zeros = Stream( lambda: 0 ); for example, will produce a stream of zeros.
#
# class Incr:
# def __init__( self, *initval ):
# if initval : self.val = initval[0]
# else: self.val = 0
# def next( self ):
# last, self.val = self.val, self.val + 1
# return last
# ints = Stream( Incr().next )
#
# will produce a range()-like sequence of numbers on demand
#
# BUT - this hasn't been tested mush for these other (infinite)
# cases. It is mostly here as a helper class for the next Class.

class Stream:
def __init__( self, nextmethod ):
self.next = nextmethod
self._buf = [ ]
def __len__( self ):
self._buf.append( self.next() )
if not self._buf[-1] :
del self._buf[-1]
return len( self._buf )
def __getitem__( self, i ):
if i >= len( self._buf ):
for j in range( len( self._buf ), i+1 ):
self._buf.append( self.next() )
return self._buf[i]
def __getslice__( self, i, j ):
junk = self.__getitem__( j )
return self._buf[i:j]

# Finally, here is the point of the whole exercise.
# A file wrapper class that
# coerces a File object's readlines() method into a
# deferred evaluation stream object.
#

class Rfile( File ):
def __init__( self, fileobj ):
File.__init__( self, fileobj )
self._stream = Stream( self.readline )
self.readlines = self.stream
# can't just assign readlines to the stream - it
# has to be a function, so we assign it to a function
# that returns the stream. Does this make any sense to you?
def stream( self ):
return self._stream

import sys

def test(f):
print 'Reading lines from ( end with a ^D ) ',f
for x in f.readlines(): print '<*'+x[:-1]+'*>'
print '<end.>'

#
# And the behaviour of readlines() in these two tests should
# demonstrate the point of the whole thing.
#
if __name__ == '__main__' :
test( sys.stdin )
test( Rfile( sys.stdin ))

####--------------

Note that the first readlines method reads to the end of file and
returns a list of lines, while the second readlines method returns
an sequence object that returns it's values on demand.

$ ./file.py
Reading lines from ( end with a ^D ) <open file '<stdin>', mode 'r' at 20069548>
TESTING
ONE
TWO
THREE
...

<*TESTING*>
<*ONE*>
<*TWO*>
<*THREE*>
<*...*>
<end.>
Reading lines from ( end with a ^D ) <open file '<stdin>', mode 'r' at 20069548>
Testing
<*Testing*>
One
<*One*>
Two
<*Two*>
Three
<*Three*>
...
<*...*>

<end.>

All this, just 'cause I hate loops like:

line = file.readline()
while line:
...
line = file.readline()

and:

while 'True' :
line = file.readline()
if not line : break
...

! :-)