簡體   English   中英

將標准輸出重定向到 python 中的“無”

[英]Redirecting stdout to "nothing" in python

我有一個由足夠多的模塊組成的大型項目,每個模塊都按照標准 output 打印一些東西。 現在隨着項目規模的擴大,沒有大的。 print語句在標准輸出上打印了很多,這使得程序相當慢。

所以,我現在想在運行時決定是否將任何內容打印到標准輸出。 我無法對模塊進行更改,因為它們有很多。 (我知道我可以將標准輸出重定向到一個文件,但即使這樣也相當慢。)

所以我的問題是如何將標准輸出重定向到無,即如何使print語句什么都不做?

# I want to do something like this.
sys.stdout = None         # this obviously will give an error as Nonetype object does not have any write method.

目前我唯一的想法是制作一個 class,它有一個 write 方法(什么都不做),並將標准輸出重定向到這個 class 的實例。

class DontPrint(object):
    def write(*args): pass

dp = DontPrint()
sys.stdout = dp

python 中有內置機制嗎? 或者還有什么比這更好的嗎?

跨平台:

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

在 Windows 上:

f = open('nul', 'w')
sys.stdout = f

在 Linux 上:

f = open('/dev/null', 'w')
sys.stdout = f

一個很好的方法是創建一個小型上下文處理器,將打印件包裝在其中。然后,您只需在with語句中使用 is 即可使所有 output 靜音。

Python 2:

import os
import sys
from contextlib import contextmanager

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

with silence_stdout():
    print("will not print")

print("this will print")

Python 3.4+:

Python 3.4 內置了這樣的上下文處理器,因此您可以像這樣簡單地使用 contextlib:

import contextlib

with contextlib.redirect_stdout(None):
    print("will not print")

print("this will print")

如果您想要隱藏的代碼使用 None 作為重定向目標直接寫入 sys.stdout 將不起作用。 相反,您可以使用:

import contextlib
import sys
import os

with contextlib.redirect_stdout(open(os.devnull, 'w')):
    sys.stdout.write("will not print")

sys.stdout.write("this will print")

如果您的代碼寫入 stderr 而不是 stdout,則可以使用 contextlib.redirect_stderr 而不是 redirect_stdout。


運行此代碼僅打印 output 的第二行,而不是第一行:

$ python test.py
this will print

這適用於跨平台(Windows + Linux + Mac OSX),並且比其他答案更干凈。

如果您使用的是 python 3.4 或更高版本,則使用標准庫有一個簡單且安全的解決方案:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")

(至少在我的系統上)似乎寫入 os.devnull 比寫入 DontPrint class 快 5 倍,即

#!/usr/bin/python
import os
import sys
import datetime

ITER = 10000000
def printlots(out, it, st="abcdefghijklmnopqrstuvwxyz1234567890"):
   temp = sys.stdout
   sys.stdout = out
   i = 0
   start_t = datetime.datetime.now()
   while i < it:
      print st
      i = i+1
   end_t = datetime.datetime.now()
   sys.stdout = temp
   print out, "\n   took", end_t - start_t, "for", it, "iterations"

class devnull():
   def write(*args):
      pass


printlots(open(os.devnull, 'wb'), ITER)
printlots(devnull(), ITER)

給出了以下 output:

<open file '/dev/null', mode 'wb' at 0x7f2b747044b0> 
   took 0:00:02.074853 for 10000000 iterations
<__main__.devnull instance at 0x7f2b746bae18> 
   took 0:00:09.933056 for 10000000 iterations

如果您在 Unix 環境(包括 Linux)中,您可以將 output 重定向到/dev/null

python myprogram.py > /dev/null

對於 Windows:

python myprogram.py > nul

這個怎么樣:

from contextlib import ExitStack, redirect_stdout
import os

with ExitStack() as stack:
    if should_hide_output():
        null_stream = open(os.devnull, "w")
        stack.enter_context(null_stream)
        stack.enter_context(redirect_stdout(null_stream))
    noisy_function()

這使用 contextlib 模塊中的功能來隱藏您嘗試運行的任何命令的output ,具體取決於 should_hide_output should_hide_output()的結果,然后在 ZC1C425268E68385D1 運行完成后恢復 output 行為。

如果要隱藏標准錯誤contextlib ,則從 contextlib 導入redirect_stderr並添加一行stack.enter_context(redirect_stderr(null_stream))

主要缺點是這只適用於 Python 3.4 及更高版本。

sys.stdout = None

print()的情況是可以的。 但是如果你調用 sys.stdout 的任何方法,例如sys.stdout.write() ,它可能會導致錯誤。

文檔中有一條注釋

在某些情況下,stdin、stdout 和 stderr 以及原始值stdinstdoutstderr可以為 None。 未連接到控制台的 Windows GUI 應用程序和使用 pythonw 啟動的 Python 應用程序通常是這種情況。

你可以嘲笑它。

import mock

sys.stdout = mock.MagicMock()

iFreilicht 答案的補充 - 它適用於 python 2 和 3。

import sys

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

class StdoutIgnore:
    def __enter__(self):
        self.stdout_saved = sys.stdout
        sys.stdout = NonWritable()
        return self

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

with StdoutIgnore():
    print("This won't print!")

您的 class 可以正常工作(除了write()方法名稱 - 它需要稱為write() ,小寫)。 只需確保將sys.stdout的副本保存在另一個變量中即可。

如果你在 *NIX 上,你可以做sys.stdout = open('/dev/null') ,但這比滾動你自己的 class 更不便攜。

流程中有許多好的答案,但這是我的 Python 3 答案(當不再支持 sys.stdout.fileno() 時):

import os
import sys
oldstdout = os.dup(1)
oldstderr = os.dup(2)
oldsysstdout = sys.stdout
oldsysstderr = sys.stderr

# Cancel all stdout outputs (will be lost) - optionally also cancel stderr
def cancel_stdout(stderr=False):
    sys.stdout.flush()
    devnull = open('/dev/null', 'w')
    os.dup2(devnull.fileno(), 1)
    sys.stdout = devnull
    if stderr:
        os.dup2(devnull.fileno(), 2)
        sys.stderr = devnull

# Redirect all stdout outputs to a file - optionally also redirect stderr
def reroute_stdout(filepath, stderr=False):
    sys.stdout.flush()
    file = open(filepath, 'w')
    os.dup2(file.fileno(), 1)
    sys.stdout = file
    if stderr:
        os.dup2(file.fileno(), 2)
        sys.stderr = file

# Restores stdout to default - and stderr
def restore_stdout():
    sys.stdout.flush()
    sys.stdout.close()
    os.dup2(oldstdout, 1)
    os.dup2(oldstderr, 2)
    sys.stdout = oldsysstdout
    sys.stderr = oldsysstderr

要使用它:

  • 使用以下命令取消所有 stdout 和 stderr 輸出:

    取消標准輸出(標准錯誤=真)

  • 將所有標准輸出(但不是標准錯誤)路由到一個文件:

    reroute_stdout('output.txt')

  • 要恢復標准輸出和標准錯誤:

    恢復標准輸出()

你為什么不試試這個?

sys.stdout.close()
sys.stderr.close()

將在此處為眾多答案添加一些示例:

import argparse
import contextlib

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

parser = argparse.ArgumentParser(description='my program')
parser.add_argument("-p", "--param", help="my parameter", type=str, required=True)

#with contextlib.redirect_stdout(None): # No effect as `argparse` will output to `stderr`
#with contextlib.redirect_stderr(None): # AttributeError: 'NoneType' object has no attribute 'write'
with contextlib.redirect_stderr(NonWritable): # this works!
    args = parser.parse_args()

正常的 output 將是:

>python TEST.py
usage: TEST.py [-h] -p PARAM
TEST.py: error: the following arguments are required: -p/--param

如果您不想處理資源分配或滾動您自己的 class,您可能需要使用來自TextIO類型的 TextIO。 默認情況下,它為您提供了所有必需的方法。

import sys
from typing import TextIO

sys.stdout = TextIO()

我用這個。 stdout重定向到一個字符串,您隨后會忽略該字符串。 我使用上下文管理器來保存和恢復stdout的原始設置。

from io import StringIO
...
with StringIO() as out:
    with stdout_redirected(out):
        # Do your thing

其中stdout_redirected定義為:

from contextlib import contextmanager

@contextmanager
def stdout_redirected(new_stdout):
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield None
    finally:
        sys.stdout = save_stdout

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM