0

I define one class Device with lot of subclasses like for example Microcontroller.

Device has two mode and especially the "simulated" mode.

I am trying to code Device class such has if Microcontroller is in simulated mode then when a print is performed it should prefix the string by [simulated]:

print "Hey !!"
> [simulated] Hey!!

I don't know how to start and if it's possible to overload print.

Katsu
  • 1,868
  • 4
  • 19
  • 28
  • You're going to need to create your own print function that checks if Microcontroller is in simulator made, and if so, adds the necessary prefix. – Ryan May 16 '14 at 14:16
  • Ok but is it possible to overload print ? (like for operator) – Katsu May 16 '14 at 14:23
  • @Katsu you can't overload print as a statement, you have to do a function yourself. – Jacobo de Vera May 16 '14 at 14:46
  • That's what `__repr__` is for: http://stackoverflow.com/questions/7784148/understanding-repr-function-in-python – LetMeSOThat4U May 16 '14 at 15:22
  • @Katsu Overloading print is only possible in Python 3.x. See this question for details: http://stackoverflow.com/questions/550470/overload-print-python If you are using Python 2.x (which it looks like) then you can use a stdout hook or logging to achieve similar effects. – ebarr May 16 '14 at 15:22
  • @JohnDoe How does `__repr__` help here? `repr` is for representing objects, not overloading the `print` statement. – ebarr May 16 '14 at 15:25

2 Answers2

0

You can implement some logger class, and use it for logging. You can do dome object injection to replace the type of logger when you want. For example:

class Logger:
    def __init__(self, prefix):
        self.prefix = prefix
    def log(self, msg):
        print('[{}] {}'.format(self.prefix, msg))


class Base:
    def set_logger(self, logger):
        self.logger = logger


class Test(Base):
    def do_something(self):
        self.logger.log('Hey !!')


test = Test()

test.set_logger(Logger('simulated'))
test.do_something()

test.set_logger(Logger('very real'))
test.do_something()

for example:

[simulated] Hey !!
[very real] Hey !!
0

I'll preface this answer by suggesting that you never use this approach in real code. However, for completeness (and as it answers the original question) I thought I would add it here.

So you can redirect and catch print statements on the fly by redirecting sys.stdout.

You can start by defining a file-like object that catches stdout and redirects to... stdout.

import sys

class OutputFormatter(object):
    def __init__(self,prefix="",suffix=""):
        self.prefix = prefix
        self.suffix = suffix

    def write(self,msg):

        # As you want to change the behaviour of the print statement,
        # you need to ignore solitary newlines that are sent to stdout

        if msg != '\n':

           # reformat the message passed by print
            msg = " ".join([self.prefix,msg,self.suffix])

        # redirect back to stdout
        sys.__stdout__.write(msg)

With this in place, you can use it to replace sys.stdout to produce the "overridden" type of print behaviour you describe.

class Device(object):
    def set_simulated(self):
        # in simulated mode, use the stdout redirect
        sys.stdout = OutputFormatter(prefix="[simulated]",suffix="")

    def set_real(self):
        # in real mode make sure to return to proper stdout
        sys.stdout = sys.__stdout__


class Microcontroller(Device):
    def __init__(self):
        super(Microcontroller,self).__init__()
        self.set_simulated()    
        print "This is simulated"

        self.set_real()
        print "This is real"

Microcontroller()

This will print the following to the terminal:

[simulated] This is simulated 
This is real

Beware however that this is a global change to the behaviour of stdout and will affect all print statements if a Device subclass has been set to simulated mode.

A much better approach for systems like this where you require context dependent messages is to use the logging module or to take the approach suggested by BSH.

ebarr
  • 7,704
  • 1
  • 29
  • 40