Recipe 2.13. Using a C++-like iostream Syntax
Credit: Erik Max Francis
Problem
You like the C++ approach to I/O, based on
ostreams and manipulators
(special objects that cause special effects on a stream when inserted
in it) and want to use it in your Python programs.
Solution
Python lets you overload operators by having your classes define
special methods (i.e., methods whose names start and end with two
underscores). To use << for output, as you
do in C++, you just need to code an output stream class that defines
the special method _ _lshift_
_:
class IOManipulator(object):
def _ _init_ _(self, function=None):
self.function = function
def do(self, output):
self.function(output)
def do_endl(stream):
stream.output.write('\n')
stream.output.flush( )
endl = IOManipulator(do_endl)
class OStream(object):
def _ _init_ _(self, output=None):
if output is None:
import sys
output = sys.stdout
self.output = output
self.format = '%s'
def _ _lshift_ _(self, thing):
''' the special method which Python calls when you use the <<
operator and the left-hand operand is an OStream '''
if isinstance(thing, IOManipulator):
thing.do(self)
else:
self.output.write(self.format % thing)
self.format = '%s'
return self
def example_main( ):
cout = OStream( )
cout<< "The average of " << 1 << " and " << 3 << " is "
<< (1+3)/2 <<endl
# emits The average of 1 and 3 is 4
if _ _name_ _ == '_ _main_ _':
example_main( )
Discussion
Wrapping Python file-like objects to emulate C++ ostreams syntax is
quite easy. This recipe shows how to code the insertion operator
<< for this purpose. The recipe also implements an
IOManipulator class (as in C++) to call arbitrary
functions on a stream upon insertion, and a predefined manipulator
endl (guess where that name comes from) to write a
newline and flush the stream.The reason class OStream's
instances hold a format attribute and reset it to
the default value '%s' after each
self.output.write is so that you can build devious
manipulators that temporarily save formatting state on the stream
object, such as:
def do_hex(stream):Some people detest C++'s cout << something
stream.format = '%x'
hex = IOManipulator(do_hex)
cout << 23 << ' in hex is ' << hex << 23
<< ', and in decimal ' << 23 << endl
# emits 23 in hex is 17, and in decimal 23
syntax, some love it. In cases such as the example given in the
recipe, this syntax ends up simpler and more readable than:
print>>somewhere, "The average of %d and %d is %f\n" % (1, 3, (1+3)/2)which is the "Python-native"
alternative (looking a lot like C in this case). It depends in part
on whether you're more used to C++ or to C. In any
case, this recipe gives you a choice! Even if you
don't end up using this particular approach,
it's still interesting to see how simple operator
overloading is in Python.
See Also
Library Reference and Python in a
Nutshell docs on file objects and
special methods such as _ _lshift_ _; Recipe 4.20 implements a Python version
of C's printf function.