Mengarahkan stdout ke "tidak ada" dengan python

128

Saya memiliki proyek besar yang terdiri dari sejumlah besar modul, masing-masing mencetak sesuatu dengan output standar. Sekarang karena proyek telah tumbuh dalam ukuran, ada yang tidak besar. dari printpernyataan mencetak banyak di luar std yang telah membuat program lebih lambat.

Jadi, saya sekarang ingin memutuskan pada saat runtime apakah akan mencetak sesuatu ke stdout atau tidak. Saya tidak dapat membuat perubahan dalam modul karena ada banyak dari mereka. (Saya tahu saya bisa mengarahkan stdout ke file tetapi bahkan ini sangat lambat.)

Jadi pertanyaan saya adalah bagaimana cara mengarahkan stdout ke nol yaitu bagaimana saya membuat printpernyataan tidak melakukan apa-apa?

# 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.

Saat ini satu-satunya ide yang saya miliki adalah membuat kelas yang memiliki metode tulis (yang tidak melakukan apa-apa) dan mengarahkan stdout ke turunan dari kelas ini.

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

dp = DontPrint()
sys.stdout = dp

Apakah ada mekanisme inbuilt di python untuk ini? Atau ada sesuatu yang lebih baik dari ini?

Pushpak Dagade
sumber
1
Saya baru saja menemukan sesuatu yang aneh. Mengubah sys.stdout menjadi Tidak ada yang benar-benar berfungsi dalam program saya, meskipun saya tidak yakin mengapa. Saya mengalami masalah yang sangat aneh dengan pengodean pengalihan ke os.devnull jadi saya mencoba hanya menggunakan Tidak ada, dan berhasil. Mungkin ada hubungannya dengan saya melakukannya dalam tes unit Django, tapi saya tidak yakin.
Teekin

Jawaban:

233

Lintas-platform:

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

Di Windows:

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

Di Linux:

f = open('/dev/null', 'w')
sys.stdout = f
Andrew Clark
sumber
Lalu bagaimana Anda membatalkannya? Apakah ada cara untuk menghidupkan kembali pernyataan cetak setelah Anda selesai?
jj172
4
Tentu, simpan saja referensi ke aslinya sys.stdoutdan atur kembali setelahnya. Misalnya old_stdout = sys.stdoutsebelum salah satu kode lainnya, maka sys.stdout = old_stdoutketika Anda selesai.
Andrew Clark
1
Catatan: itu tidak mengarahkan ulang pada tingkat deskriptor file yaitu, itu mungkin gagal untuk mengarahkan ulang output dari proses anak eksternal, output dari ekstensi C yang mencetak langsung ke C stdout os.write(1, b'abc\n'),. Anda perlu os.dup2(), untuk mengarahkan ulang di tingkat deskriptor file, lihatstdout_redirected()
jfs
4
Perhatikan ada sys.__stdout__untuk membatalkan pengalihan. Jadi Anda cukup melakukannya sys.stdout = sys.__stdout__dan tidak perlu menyimpan cadangan.
Konstantin Sekeresh
28

Cara yang baik untuk melakukan ini adalah membuat prosesor konteks kecil yang Anda bungkus cetakan Anda. Anda kemudian hanya menggunakan dalam- withpernyataan untuk membungkam semua 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 memiliki pengolah konteks seperti ini bawaan, jadi Anda cukup menggunakan contextlib seperti ini:

import contextlib

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

print("this will print")

Menjalankan kode ini hanya mencetak output baris kedua, bukan yang pertama:

$ python test.py
this will print

Ini bekerja lintas-platform (Windows + Linux + Mac OSX), dan lebih bersih daripada yang lainnya.

Emil Stenström
sumber
@celticminstrel Anda benar, terima kasih telah mengoreksi saya. Saya telah memperbarui kode untuk mengatur ulang sys.stdout sesudahnya.
Emil Stenström
Ini melewatkan beberapa pengecekan kesalahan, tapi ide bagus
Gila Fisikawan
Menambahkan kode untuk menunjukkan cara kerjanya di Python 3 juga.
Emil Stenström
15

Jika Anda menggunakan python 3.4 atau lebih tinggi, ada solusi sederhana dan aman menggunakan perpustakaan standar:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")
iFreilicht
sumber
3
with contextlib.redirect_stdout(None)berfungsi juga
Chris Cogdon
@ ChrisCogdon Petunjuk yang sangat bagus, saya mengedit jawaban yang sesuai.
iFreilicht
1
Tidak berfungsi jika Anda menggunakan pustaka cetak. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy
@Speeeddy apakah Anda mendapatkan kesalahan ini dengan python 2? Anda bisa membuat kelas dengan writemetode dummy yang tidak melakukan apa-apa. Lihat jawaban saya di bawah ini.
dizcza
8

(setidaknya pada sistem saya) tampaknya menulis ke os.devnull sekitar 5x lebih cepat daripada menulis ke kelas DontPrint, yaitu

#!/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)

memberikan hasil sebagai berikut:

<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
DonP
sumber
4
Sebagai catatan saja, lain kali Anda ingin mengatur
Antoine Pelisse
6

Jika Anda berada di lingkungan Unix (termasuk Linux), Anda dapat mengarahkan output ke /dev/null:

python myprogram.py > /dev/null

Dan untuk Windows:

python myprogram.py > nul
Nick Radford
sumber
2
Lebih baik memiliki solusi yang akan bekerja pada semua platform.
Pushpak Dagade
2
@ Guanidene, mungkin, tetapi jika dia adalah satu-satunya orang yang menggunakan programnya, dia bisa menjamin lingkungan.
Nick Radford
nul = myfunc() ?
Hicsy
2

Bagaimana dengan ini:

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()

Ini menggunakan fitur-fitur dalam modul contextlib untuk menyembunyikan output dari perintah apa pun yang Anda coba jalankan, tergantung pada hasil should_hide_output(), dan kemudian mengembalikan perilaku output setelah fungsi tersebut selesai dijalankan.

Jika Anda ingin menyembunyikan output kesalahan standar, maka impor redirect_stderrdari contextlibdan tambahkan pepatah baris stack.enter_context(redirect_stderr(null_stream)).

Kelemahan utama bahwa ini hanya berfungsi di Python 3.4 dan versi yang lebih baru.

Elias Zamaria
sumber
1

Kelas Anda akan berfungsi dengan baik (dengan pengecualian write()nama metode - perlu dipanggil write(), huruf kecil). Pastikan Anda menyimpan salinan dari sys.stdoutvariabel lain.

Jika Anda menggunakan * NIX, Anda bisa melakukannya sys.stdout = open('/dev/null'), tetapi ini lebih portabel daripada menggulung kelas Anda sendiri.

Rafe Kettler
sumber
1

Anda bisa mengejeknya.

import mock

sys.stdout = mock.MagicMock()
Karthik Raja
sumber
0

Kenapa kamu tidak mencoba ini?

sys.stdout.close()
sys.stderr.close()
Veerendra Kakumanu
sumber
Karena A) bahkan mungkin tidak berfungsi, dan B) jika itu terjadi, Anda akan mendapatkan kesalahan alih-alih hanya menekan output.
Fisikawan Gila
0
sys.stdout = None

Tidak masalah untuk print()kasus ini. Tetapi itu dapat menyebabkan kesalahan jika Anda memanggil metode sys.stdout, misalnya sys.stdout.write().

Ada catatan dalam dokumen :

Dalam beberapa kondisi stdin, stdout dan stderr serta nilai asli stdin , stdout dan stderr dapat berupa None. Biasanya demikian untuk aplikasi Windows GUI yang tidak terhubung ke konsol dan aplikasi Python dimulai dengan pythonw.

Alexander Chzhen
sumber
"OK" dan "Tidak akan membuat kesalahan saat Anda mencoba mencetak" adalah hal yang sangat berbeda dalam kasus ini.
Fisikawan Gila
1
Kamu benar. Anda tidak dapat memanggil metode apa pun sys.stdout = Nullsehingga dalam beberapa kasus itu bisa berbahaya.
Alexander Chzhen
Dalam kasus yang tepat ini diketahui berbahaya. OP secara khusus menangani kesalahan yang didapatnya dari melakukannya.
Fisikawan Gila
0

Tambahan untuk jawaban iFreilicht - ini berfungsi untuk kedua 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!")
dizcza
sumber