10

I have a large chunk of code which uses the "print" statement. As to say in this way:

print "foo"

and not

print("foo")

I want to alter the output. Can I do this without changing all the lines with print? For example by overwriting the function/statement?

Taylan Aydinli
  • 4,333
  • 15
  • 39
  • 33
user3099519
  • 103
  • 1
  • 1
  • 5

4 Answers4

15

Python directly supports what you want to do:

from __future__ import print_function

Any module with that line at the top will treat the print statement as a function instead, making the code compatible with both Python 2 and 3.

This applies just to the print statement; you cannot override other statements.

This does mean you then have to use print() as a function everywhere in that module, but you can then also provide your own implementation if you so desire:

from __future__ import print_function
import __builtin__

def print(*args, **kwargs):
    __builtin__.print('Prefixed:', *args, **kwargs)

print('Hello world!')

Another option is to use a context manager to capture printed statements, directing the output away from sys.stdout into a in-memory file object of your choosing:

from contextlib import contextmanager
import sys
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO


@contextmanager
def capture_sys_output():
    caputure_out = StringIO()
    current_out = sys.stdout
    try:
        sys.stdout = caputure_out
        yield caputure_out
    finally:
        sys.stdout = current_out

and wrap any blocks that you want to capture print output for with the context manager. Here is an example prefixing printed lines:

with capture_sys_output as output:
    print 'Hello world!'

output = output.get_value()
for line in output.splitlines():
    print 'Prefixed:', line

or even provide a wrapper:

from contextlib import contextmanager
import sys

class Prefixer(object):
    def __init__(self, prefix, orig):
        self.prefix = prefix
        self.orig = orig
    def write(self, text):
        self.orig.write(self.prefix + text)
    def __getattr__(self, attr):
        return getattr(self.orig, attr)     

@contextmanager
def prefix_stdout(prefix):
    current_out = sys.stdout
    try:
        sys.stdout = Prefixer(prefix, current_out)
        yield
    finally:
        sys.stdout = current_out

and use as:

with prefix_stdout('Prefixed: '):
    print 'Hello world!'

but take into account that print statements usually write data to stdout in separate chunks; the newline at the end is a separate write.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • @RemcoGerlich: Yes it is, really. – Martijn Pieters Dec 13 '13 at 13:26
  • Ahh, and then he can go ahead and overwrite the builtin. I'd still prefer switching sys.stdout, but who knows. – RemcoGerlich Dec 13 '13 at 13:27
  • 1
    The problem is that in that case, he has to change all his print statements to use parens, and changing all his print statements is exactly what he's trying to avoid. – RemcoGerlich Dec 13 '13 at 13:28
  • @RemcoGerlich: But it'll be much easier to maintain than using a global 'swap out `stdout`' hammer. – Martijn Pieters Dec 13 '13 at 13:29
  • @RemcoGerlich, it's better to be explicit, and use the print function (and make a one-time effort of replacing the code), than face implicit consequences of changes global variables. – Maciej Gol Dec 13 '13 at 13:29
  • If he's already changing all the print()s, he can also change them to call his_own_custom_print() instead and doesn't need to overwrite anything, but he's asking how to avoid doing that work. – RemcoGerlich Dec 13 '13 at 13:30
  • @RemcoGerlich, perhaps it could take more effort and time to do what the OP wants, fixing the side-effects on code called by the mentioned code, than replacing the strings and be on the safe side. – Maciej Gol Dec 13 '13 at 13:33
  • @RemcoGerlich: you can name the override `print()` too.. – Martijn Pieters Dec 13 '13 at 13:37
  • Context manager decorator... I like it! – Vincent Dec 13 '13 at 13:40
9

This changes the behavior of the print statement without forcing you to change

print "foo"

to

print("foo")

import sys
_stdout = sys.stdout

class MyStream(object):
    def __init__(self, target):
        self.target = target

    def write(self, s):
        s = 'Foo : {!r}'.format(s)
        self.target.write(s)
sys.stdout = MyStream(sys.stdout)
print 'Hi'
sys.stdout = _stdout   # return print to its old behavior

yields

Foo : 'Hi'

This could be neatened with a context manager, but if you don't want to change your print statements into print functions, you probably don't want to have to wrap your print statements in a context manager either.


So, a better, a more civilized way would be to use

2to3 --write  --fix print test.py

to automatically change all the print statements in your code (e.g. test.py above) into print functions. Then you can change the behaviour by redefining the print function:

from __future__ import print_function
import __builtin__

def print(*args, **kwargs): 
    __builtin__.print('Foo:', *args, **kwargs)
print('Hi')

yields

Foo: Hi
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
4

In Python 2.x, print is a statement and so it can be called without parens. Apparently you're using that.

You can't overwrite the built-in print statement.

However, it writes its output to sys.stdout. Perhaps you can overwrite sys.stdout with some object that has a .write method, like this:

import sys
from StringIO import StringIO

s = StringIO()
sys.stdout = s

print "Hmmm."

assert s.getvalue() == "Hmmm.\n"
RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79
  • 1
    Actually, you **can**. *Just* the `print` statement, mind you, but you can. – Martijn Pieters Dec 13 '13 at 13:26
  • 1
    Note that this will also alter _all_ stdout operations done by the code _called_ by that large chunk of code OP mentioned. This is not isolated only to the mentioned code's scope. – Maciej Gol Dec 13 '13 at 13:28
3

In Python 2, there is the print statement, which is a language construct. As such, you won’t be able to overwrite its behavior.

In Python 3, and in Python 2 if you enable it explicitely, you have the print function. It does require the parentheses, so print "foo" will not work, but you can redefine what it does:

>>> from __future__ import print_function # for Python 2
>>> oldPrintFunction = print
>>> def print (*args, **kwargs):
        oldPrintFunction('I am a changed print function')
        oldPrintFunction(*args, **kwargs)

>>> print('foo')
I am a changed print function
foo
poke
  • 369,085
  • 72
  • 557
  • 602