Suppress calls to print (python)

2020-01-24 06:36发布

问题:

Is there a way to stop a function from calling print?


I am using the pygame.joystick module for a game I am working on.

I created a pygame.joystick.Joystick object and in the actual loop of the game call its member function get_button to check for user input. The function does everything I need it to do, but the problem is that it also calls print, which slows down the game considerably.

Can I block this call to print?

回答1:

Python lets you overwrite standard output (stdout) with any file object. This should work cross platform and write to the null device.

import sys, os

# Disable
def blockPrint():
    sys.stdout = open(os.devnull, 'w')

# Restore
def enablePrint():
    sys.stdout = sys.__stdout__


print 'This will print'

blockPrint()
print "This won't"

enablePrint()
print "This will too"

If you don't want that one function to print, call blockPrint() before it, and enablePrint() when you want it to continue. If you want to disable all printing, start blocking at the top of the file.



回答2:

Based on @FakeRainBrigand solution I'm suggesting a safer solution:

import os, sys

class HiddenPrints:
    def __enter__(self):
        self._original_stdout = sys.stdout
        sys.stdout = open(os.devnull, 'w')

    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout.close()
        sys.stdout = self._original_stdout

Then you can use it like this:

with HiddenPrints():
    print("This will not be printed")

print("This will be printed as before")

This is much safer because you can not forget to re-enable stdout, which is especially critical when handling exceptions.

# This is an example of not-so-good solution
# without 'with' context manager statement.
try:
    disable_prints()
    something_throwing()
    # enable_prints() This wouldn't be enough!
except ValueError:
    handle_error()
finally:
    enable_prints() # That's where it needs to go.

If you forgot the finally clause, none of your print calls would print anything anymore. Using the with statement, that can not happen.

It is not safe to use sys.stdout = None, because someone could call methods like sys.stdout.write()



回答3:

As @Alexander Chzhen suggested, using a context manager would be safer than calling a pair of state-changing functions.

However, you don't need to reimplement the context manager - it's already in the standard library. You can redirect stdout (the file object that print uses) with contextlib.redirect_stdout, and also stderr with contextlib.redirect_stderr.

import os
import contextlib

with open(os.devnull, "w") as f, contextlib.redirect_stdout(f):
    print("This won't be printed.")


回答4:

No, there is not, especially that majority of PyGame is written in C.

But if this function calls print, then it's PyGame bug, and you should just report it.



回答5:

I have had the same problem, and I did not come to another solution but to redirect the output of the program (I don't know exactly whether the spamming happens on stdout or stderr) to /dev/null nirvana.

Indeed, it's open source, but I wasn't passionate enough to dive into the pygame sources - and the build process - to somehow stop the debug spam.

EDIT :

The pygame.joystick module has calls to printf in all functions that return the actual values to Python:

printf("SDL_JoystickGetButton value:%d:\n", value);

Unfortunately you would need to comment these out and recompile the whole thing. Maybe the provided setup.py would make this easier than I thought. You could try this...



回答6:

If you want to block print calls made by a particular function, there is a neater solution using decorators. Define the following decorator:

# decorater used to block function printing to the console
def blockPrinting(func):
    def func_wrapper(*args, **kwargs):
        # block all printing to the console
        sys.stdout = open(os.devnull, 'w')
        # call the method in question
        value = func(*args, **kwargs)
        # enable all printing to the console
        sys.stdout = sys.__stdout__
        # pass the return value of the method back
        return value

    return func_wrapper

Then just place @blockPrinting before any function. For example:

# This will print
def helloWorld():
    print("Hello World!")
helloWorld()

# This will not print
@blockPrinting
def helloWorld2():
    print("Hello World!")
helloWorld2()


回答7:

A completely different approach would be redirecting at the command line. If you're on Windows, this means a batch script. On Linux, bash.

/full/path/to/my/game/game.py > /dev/null
C:\Full\Path\To\My\Game.exe > nul

Unless you're dealing with multiple processes, this should work. For Windows users this could be the shortcuts you're creating (start menu / desktop).



回答8:

The module I used printed to stderr. So the solution in that case would be:

sys.stdout = open(os.devnull, 'w')


回答9:

Based on @Alexander Chzhen solution, I present here the way to apply it on a function with an option to suppress printing or not.

    import os, sys
    class SuppressPrints:
        #different from Alexander`s answer
        def __init__(self, suppress=True):
            self.suppress = suppress

        def __enter__(self):
            if self.suppress:
                self._original_stdout = sys.stdout
                sys.stdout = open(os.devnull, 'w')

        def __exit__(self, exc_type, exc_val, exc_tb):
            if self.suppress:
                sys.stdout.close()
                sys.stdout = self._original_stdout
    #implementation
    def foo(suppress=True):
        with SuppressPrints(suppress):
            print("It will be printed, or not")

    foo(True)  #it will not be printed
    foo(False) #it will be printed

I hope I can add my solution below answer of Alexander as a comment, but I don`t have enough (50) reputations to do so.