Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to "overload" python's print function "globally"?

I am using python 2.6.6 and I need to overload the default python print function. I need to do it because this code may be used on a system where a built-in function has to be used to generate output, otherwise no output is displayed.

So, just for example, if you have a python script like this:

from __future__ import print_function

def NewPrint(Str):
    with open("somefile.txt","a") as AFile:
        AFile.write(Str)

def OverloadPrint():
    global print
    print = NewPrint

OverloadPrint()
print("ha")

It works fine. The input to the "overloaded" print is in the file specified by NewPrint.

Now with that in mind I would like to be able to run the couple of lines above and have print to do what NewPrint does during the entire execution of the script. Right now if I call a function from another module that uses print it will use the built-in print and not the one I just overwrote. I guess this has something to do with namespaces and built-ins, but my python is not good enough.

Edit:

I tried to keep simple, but looks like this caused more confusion...

  1. The function that overloads print will print to a GUI, so no need to care about redirection IF the function is overloaded.
  2. I know my example does not do what the print function actually does. A better example of how I am thinking of coding it is (still not great I know):

    def hli_print(*args, **kw):
        """
        print([object, ...], sep=' ', end='\n', file=sys.stdout)
        """
        sep = kw.get('sep', ' ')
        end = kw.get('end', '\n')
        File = kw.get('file', sys.stdout)
        args = [str(arg) for arg in args]
        string = sep.join(args) + end
        File.write(string)
        hli_Print(string)
    
  3. From 2 above you can see the function I have to use to print to the GUI "hli_Print" it is a C++ function exposed through a swig wrapper.

  4. We only use the standard library and our own swig wrappers, also our developers use print as a function (getting used to 3.X). So I did not really worry much about some other module calling print and having something else instead.

From all the comments I guess that just using some print_() function instead of print() (which is what we currently do) may be the best, but I got really curious to see if in python it would be possible to do what I described.

like image 766
Mac Avatar asked Dec 14 '12 00:12

Mac


People also ask

Can we override print function in Python?

By default, Python's print statement ends each string that is passed into the function with a newline character, \n . This behavior can be overridden with the function's end parameter, which is the core of this method.

How do you print all output in Python?

Python print() Function The print() function prints the specified message to the screen, or other standard output device. The message can be a string, or any other object, the object will be converted into a string before written to the screen.

How many arguments can the print () function take?

You saw print() called without any arguments to produce a blank line and then called with a single argument to display either a fixed or a formatted message. However, it turns out that this function can accept any number of positional arguments, including zero, one, or more arguments.


2 Answers

As @abarnert's answer and several comments have pointed out, replacing print is probably not a good idea. But just for the sake of completeness, here's why your code was not successfully overriding it for other modules.

The print function is defined in the module __builtin__ (which is renamed to builtins in Python 3). The Python interpreter makes everything in the __builtin__ module's namespace available to all other code it is running without importing it into your module's own namespace. It's magical!

However, when you create your own function named print (using print = NewPrint) it doesn't overwrite the original version in __builtin__. You're just creating a new variable in your module's namespace that shadows the old one from __builtin__. The global statement doesn't help, as it only lets you tell Python to that you want to write into your module's global namespace rather than some inner namespace inside a function.

To replace the default print function, you will need to explicitly replace it in the __builtin__ module. Here's some example code that does that:

from __future__ import print_function
try:
    import __builtin__ as builtins # Python 2
except ImportError:
    import builtins # Python 3

_print = print # keep a local copy of the original print
builtins.print = lambda *args, **kwargs: _print("foo:", *args, **kwargs)

To repeat, this is really not a good idea. While making sure I understood what I was talking about in this answer, I managed to crash one of my Python sessions by replacing print with a lambda function that didn't accept the file parameter that Python uses to print to standard error. A few lines later the exception handler was not pleased when it got a second exception while trying to print another exception's traceback.

There's almost certainly a better way to get the results you want.

like image 120
Blckknght Avatar answered Oct 01 '22 20:10

Blckknght


I don't think your question makes any sense.

First, if you're running Python 2.6, everything you import, etc., will be using print statements, even if your own module is using the print function. So, overloading the function will not affect anything else.

Second, you say "I need to do it because this code may be used on a system where a built-in function has to be used to generate output, otherwise no output is displayed." Well, your NewPrint is not a built-in function, so this won't help anyway.

It's also worth noting that your NewPrint doesn't implement most of the functionality of the print function, and even the bit that it does implement, it does wrong (print(s) will print s followed by a newline). So, if you did replace the builtin print function with yours, you'd just end up breaking most of your own code and any stdlib/third-party code you depend on.

It may be that you can accomplish what you want by creating a file-like object that replaces sys.stdout. Otherwise, I can't see how anything could work. For example:

class FakeStdOut(object):
    # … lots of other stuff to implement or inherit
    def write(s):
        with open("somefile.txt", "a") as f:
            f.write(s)

def OverloadPrint():
    sys.stdout = FakeStdOut()

But even if this works, it probably isn't what you really want. For a quick&dirty script, on a platform with a defective shell, this is sometimes a handy idea. But otherwise, it will probably cause you more trouble in the long run than coming up with a better solution. Here's just a few things that can go wrong (just as examples, not an exhaustive list)

  • If you ever want to change the file the output goes to, you have to modify the script. If you instead used >> in the shell, you could just call the script differently.
  • Someone reading or debugging your code (like, say, you, three months after you forgot how it worked) will be surprised by what's going on.
  • Some stdlib/third-party/coworker/etc. code you call will check that stdout is a tty before you make the change, and configure itself for interactive output.
  • Some code will print before you got a chance to redirect, and you'll spend hours trying to figure out how to reorder things to work around the problem.
  • You have to know how to implement a 'file-like object' completely—and that concept is not fully defined in 2.6—or it will break with some code.
  • Somewhere, there's some code that you thought was printing, but it was actually, say, logging or writing to sys.stderr or doing something else, so you've given yourself a false sense of security that you're now logging everything in somefile.txt, and won't discover otherwise until 6 months later, when you desperately need that missing information to debug a problem at a customer site.

Since you've edited the question, here's some further responses:

From all the comments I guess that just using some print_() function instead of print()

Yes, that's a more reasonable option. But I probably wouldn't call it print_. And it's simpler to put the "do or do not" logic inside the function, instead of swapping implementations in and out of the global name (especially since you're going to screw that up at some point if your code isn't all in one big module).

I worked on a project with a similar use case: We had messages we wanted to go to the syslogs, and also go to a GUI "log window" if it was open. So we wrote a glog function that wrapped that up, and nobody complained that they wanted to write print instead. (In fact, at least one guy on the team was very happy that he could use print for quick-and-dirty printouts while debugging without affecting the real output, especially when he had to debug the GUI logging code.)

But that was just because we didn't have any experience with the new (back then) logging module. Nowadays, I think I'd create a logging Handler implementation that writes to the GUI window, and just add that handler, and use the standard logging methods everywhere. And it sounds like that might be the best option for you.

Also, one last probably-irrelevant side issue:

We only use the standard library and our own swig wrappers, also our developers use print as a function (getting used to 3.X).

So why not use 3.x in the first place? Obviously 3.x has the actual 3.x standard library, instead of something kind of close to the 3.x standard library if you do some __future__ statements, and SWIG works with 3.x…

like image 36
abarnert Avatar answered Oct 01 '22 18:10

abarnert