如何在Python中抑制控制台输出?

54
我正在使用Pygame/SDL的游戏手柄模块获取输入。每次我调用它的get_hat()方法时,它会将一些信息打印到控制台上。这是有问题的,因为我使用控制台来帮助我调试,现在每秒钟会有60行SDL_JoystickGetHat value:0:出现。是否有办法禁用这个功能?可以通过Pygame/SDL中的选项或者在函数调用时抑制控制台输出吗?我在Pygame文档中没有找到相关说明。
编辑:结果证明这是由于在编译SDL库时开启了调试模式。

现在我很好奇你在使用什么平台(Linux版本?),以及使用了什么软件包?或者是自己编译的吗? - Keith
这是很久以前的事了,但我当时使用的是Windows操作系统、Python2.6版本和Pygame1.9(其中包括SDL)。我只需使用他们提供的Windows安装程序,所有东西都已经编译好了。 - user102430
9个回答

70

为了完整起见,这里有一种来自Dave Smith的博客的不错解决方案:

from contextlib import contextmanager
import sys, os

@contextmanager
def suppress_stdout():
    with open(os.devnull, "w") as devnull:
        old_stdout = sys.stdout
        sys.stdout = devnull
        try:  
            yield
        finally:
            sys.stdout = old_stdout

通过这种方式,您可以在任何想要抑制输出的地方使用上下文管理:

print("Now you see it")
with suppress_stdout():
    print("Now you don't")

3
将所有的 out 替换为 err 以抑制警告和错误信息,参考 @telotortium 的回答。 - bers
1
对于Python >3.4,contextlib提供了redirect_stdout()函数,具有类似的功能。 - normanius

43
为了补充 Charles 的回答,Python 内置了两个上下文管理器:redirect_stdoutredirect_stderr,你可以使用它们将命令的输出重定向或抑制到文件或 StringIO 变量中。
import contextlib

with contextlib.redirect_stdout(None):
    do_thing()

要获取更完整的解释,请阅读文档

快速更新: 在某些情况下,传递None可能会引发一些引用错误(例如keras.models.Model.fit调用了sys.stdout.write这将是有问题的),在这种情况下,请传递io.StringIO()os.devnull


1
谢谢你的提示。你也可以将print语句作为redirect_stdout()的参数添加进去。with redirect_stdout(print('Worked!')): do_thing() - datalifenyc
2
@myidealab 既然 print 语句会返回 None,那么在 with 语句前面加一个 print 语句不就等同于直接在 with 语句前面加一吗? - Will

23

你可以通过将标准输出/错误(我不知道哪一个会是)分配给空设备来解决这个问题。在Python中,标准输出/错误文件分别为sys.stdout/sys.stderr,而空设备是os.devnull,所以你可以这样做:

sys.stdout = open(os.devnull, "w")
sys.stderr = open(os.devnull, "w")

这将完全禁用这些错误信息。不幸的是,这也会禁用所有控制台输出。为了解决这个问题,在调用get_hat()方法之前禁用输出,然后通过以下方式恢复它:

sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__

它将标准输出和错误恢复到它们的原始值。


我以前不知道这个技巧。它看起来是我想要的,但当我尝试使用它时,get_hat()函数仍然会在控制台中打印。这可能与SDL有关吗? - user102430
1
使用占位符来代替原始的sys.stdout和sys.stderr,在恢复stdout时使用,将扩展此解决方案以在存在“非标准”stdout并需要在抑制后保留的情况下工作,例如用于QGIS python控制台中的使用。 - Mr Purple
2
第一部分代码可以运行,但即使加上“恢复”代码,我仍然无法在控制台输出任何内容。我使用的是3.4.3版本的Ubuntu 14.04。 - m4p85r
谢谢你的解决方案,我的问题是输出正在减慢我的代码,这样做是否仍会导致相同的减速? - jerpint
在Linux上,os.devnull 在Python 3.6中是一个字符串。 - Mr Tsjolder
显然,它会抛出错误,AttributeError: 'module' object has no attribute '___stdout___' - hola

6

在 @charleslparker 的回答的基础上进行改进:

from contextlib import contextmanager
import sys, os

@contextmanager
def suppress_stdout():
    with open(os.devnull, "w") as devnull:
        old_stdout = sys.stdout
        sys.stdout = devnull
        try:  
            yield
        finally:
            sys.stdout = old_stdout

print("Now you see it")
with suppress_stdout():
    print("Now you don't")

测试

>>> with suppress_stdout():
        os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV')

/mnt/Vancouver/programming/scripts/PHASER.WAV:

 File Size: 1.84k     Bit Rate: 90.4k
  Encoding: Unsigned PCM  
  Channels: 1 @ 8-bit    
Samplerate: 11025Hz      
Replaygain: off         
  Duration: 00:00:00.16  

In:100%  00:00:00.16 [00:00:00.00] Out:1.79k [!=====|=====!]        Clip:0    
Done.

使用这个方法可以完全抑制os.system()的输出:

>>> with suppress_stdout():
        os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV >/dev/null 2>&1')
>>> ## successfully executed

>>> import time
>>> with suppress_stdout():
        for i in range(3):
                os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV >/dev/null 2>&1')
                time.sleep(0.5) 
>>> ## successfully executed

这对于标记长时间运行的脚本的完成非常有用。


5

我该如何通过Python禁用SDL调试?谷歌告诉我环境变量是SDL_DEBUG,但插入os.environ['SDL_DEBUG'] = '0'似乎没有任何效果。 - user102430
4
@jackson,这是 SDL 的编译时调试选项。该信息被打印出来是因为在编译 SDL 库时定义了 DEBUG 符号。 - Geoff Reedy

2

我在Windows系统中使用pythonw.exe而不是python.exe。 在其他操作系统中,您还可以将输出重定向到/dev/nul。 为了仍然能够看到我的调试输出,我正在使用日志记录模块。


1

正如Demolishun在一篇关闭的重复问题的答案中提到的那样,有一个线程讨论了这个问题。该线程来自2009年8月,其中一位开发者说调试代码是意外留下的。我从pip安装了Pygame 1.9.1,但调试输出仍然存在。

为了暂时解决这个问题,我从pygame.org下载了源代码,从src/joystick.c中删除了打印语句并编译了代码。

就我所知,我使用的是OS X 10.7.5。


0

使用os.devnull的解决方案可能会导致多进程同步问题 - 因此多个进程将等待相同的资源。至少这是我在Windows中遇到的情况。

以下解决方案可能更好:


class DummyOutput(object):
    def __init__(self, *args, **kwargs):
        pass

    def write(self, *args, **kwargs):
        pass


class suppress_stdout_stderr(object):
    def __init__(self):
        self.stdout = sys.stdout
        self.stderr = sys.stderr

    def __enter__(self, *args, **kwargs):
        out = DummyOutput()
        sys.stdout = out
        sys.stderr = out

    def __exit__(self, *args, **kwargs):
        sys.stdout = self.stdout
        sys.stderr = self.stderr


with suppress_stdout_stderr():
    print(123, file=sys.stdout)
    print(123, file=sys.stderr)

0

如果您使用Debian或Ubuntu操作系统,您可以简单地重新编译pygame以去除这些消息。

cd /tmp
sudo apt-get build-dep pygame
apt-get source pygame
vim pygame-1.9.1release+dfsg/src/joystick.c
# search for the printf("SDL.. messages and put a // in front
apt-get source --compile pygame
sudo dpkg -i python-pygame_1.9.1release+dfsg-9ubuntu1_amd64.deb

你好,Max


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接