Python'da stdout "hiçbir şeye" yönlendiriliyor


129

Yeterince çok sayıda modülden oluşan ve her biri standart çıktıya bir şeyler yazdıran büyük bir projem var. Şimdi proje boyut olarak büyüdükçe, büyük bir hayır var. arasında printönemli ölçüde daha yavaş programını yapmıştır std dışarı üzerinde çok baskı ifadeleri.

Bu yüzden, çalışma zamanında stdout'a herhangi bir şey yazdırıp yazdırmamaya karar vermek istiyorum . Modüllerde bol miktarda olduğu için değişiklik yapamıyorum. (Standart çıkışı bir dosyaya yönlendirebileceğimi biliyorum ama bu bile oldukça yavaş.)

Öyleyse sorum şu: stdout'u hiçbir şeye nasıl yönlendirebilirim, yani printifadenin hiçbir şey yapmamasını nasıl sağlayabilirim ?

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

Şu anda sahip olduğum tek fikir, yazma yöntemine sahip (hiçbir şey yapmayan) bir sınıf yapmak ve stdout'u bu sınıfın bir örneğine yönlendirmek.

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

dp = DontPrint()
sys.stdout = dp

Bunun için python'da dahili bir mekanizma var mı? Yoksa bundan daha iyi bir şey mi var?



1
Tuhaf bir şey keşfettim. Sys.stdout'u None'a dönüştürmek aslında programımda işe yarıyor, ancak neden olduğundan emin değilim. Os.devnull'a yönlendirilen kodlamayla ilgili gerçekten garip sorunlar yaşıyordum, bu yüzden sadece Yok'u kullanmayı denedim ve işe yarıyor. Bunu bir Django birim testinde yapmamla bir ilgisi olabilir, ama emin değilim.
Teekin

Yanıtlar:


233

Platformlar arası:

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

Windows'ta:

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

Linux'ta:

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

O halde nasıl iptal edersiniz? İşiniz bittikten sonra yazdırma ifadelerini tekrar açmanın bir yolu var mı?
jj172

4
Elbette, sadece orijinale bir referans kaydedin sys.stdoutve daha sonra tekrar ayarlayın. Örneğin old_stdout = sys.stdout, diğer kodların herhangi birinden önce, sonra sys.stdout = old_stdoutişiniz bittiğinde.
Andrew Clark

1
not: bir dosya tanımlayıcı seviyesinde yeniden yönlendirme yapmaz, yani harici çocuk süreçlerin çıktısını, C uzantılarının çıktılarını doğrudan C stdout'a yeniden yönlendirmede başarısız olabilir os.write(1, b'abc\n'). os.dup2()Dosya tanımlayıcı düzeyinde yeniden yönlendirme yapmanız gerekir , bkz.stdout_redirected()
jfs

4
sys.__stdout__Bir yeniden yönlendirmeyi iptal etmek için olduğunu unutmayın . Yani sys.stdout = sys.__stdout__yedeği depolamanıza gerek yok.
Konstantin Şeker

29

Bunu yapmanın güzel bir yolu, baskılarınızı içine withsardığınız küçük bir bağlam işlemcisi oluşturmaktır. Daha sonra, tüm çıktıları susturmak için yalnızca bir -statement içinde kullanırsınız .

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, bunun gibi yerleşik bir bağlam işlemcisine sahiptir, böylece contextlib'i şu şekilde kullanabilirsiniz:

import contextlib

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

print("this will print")

Bu kodu çalıştırmak, ilk çıktıyı değil, yalnızca ikinci çıktı satırını yazdırır:

$ python test.py
this will print

Bu, platformlar arası çalışır (Windows + Linux + Mac OSX) ve diğer cevaplardan daha temizdir.


@celticminstrel tamamen haklısın, beni düzelttiğin için teşekkürler. Daha sonra sys.stdout'u sıfırlamak için kodu güncelledim.
Emil Stenström

Bunda bazı hata kontrolleri eksik, ama harika bir fikir
Mad Physicist

Bunun Python 3'te de nasıl çalıştığını gösteren kod eklendi.
Emil Stenström

15

Python 3.4 veya üzerindeyseniz, standart kitaplığı kullanan basit ve güvenli bir çözüm vardır:

import contextlib

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

3
with contextlib.redirect_stdout(None)aynı zamanda çalışıyor
Chris Cogdon

@ChrisCogdon Çok iyi bir ipucu, cevabı buna göre düzenledim.
iFreilicht

1
Pprint kitaplığı kullanıyorsanız çalışmaz. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy

@Speeeddy bu hatayı python 2 ile mi aldın? writeHiçbir şey yapmayan sahte bir yöntemle bir sınıf oluşturabilirsiniz . Aşağıdaki cevabıma bakın.
dizcza

8

(en azından benim sistemimde) os.devnull'a yazmanın DontPrint sınıfına yazmaktan yaklaşık 5 kat daha hızlı olduğu görülüyor, yani

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

şu çıktıyı verdi:

<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

4
Sadece kayıt için, bir dahaki sefere zaman bir şey istiyorum, sadece kullanmak sürümüyle gelen timeit
Antoine pelisse

6

Bir Unix ortamındaysanız (Linux dahil), çıktıyı şuraya yönlendirebilirsiniz /dev/null:

python myprogram.py > /dev/null

Ve Windows için:

python myprogram.py > nul

2
Tüm platformlarda çalışacak bir çözüme sahip olmak daha iyidir.
Pushpak Dagade

2
@Guanidene, belki, ancak programını kullanan tek kişi oysa, ortamı garanti edebilir.
Nick Radford

nul = myfunc() ?
Hicsy

2

Buna ne dersin:

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

Bu, çalıştırmaya çalıştığınız komutun çıktısını, sonucuna bağlı olarak gizlemek için contextlib modülündeki özellikleri kullanır ve should_hide_output()bu işlev çalıştıktan sonra çıktı davranışını geri yükler.

Standart hata çıktısını gizlemek istiyorsanız, redirect_stderrburadan içe aktarın contextlibve bir satır ekleyin stack.enter_context(redirect_stderr(null_stream)).

Ana dezavantajı, bunun yalnızca Python 3.4 ve sonraki sürümlerde çalışmasıdır.



1

Sınıfınız gayet iyi çalışacaktır ( write()yöntem adı dışında - write()küçük harfle çağrılması gerekir ). Sadece bir kopyasını sys.stdoutbaşka bir değişkene kaydettiğinizden emin olun .

* NIX kullanıyorsanız, yapabilirsiniz sys.stdout = open('/dev/null'), ancak bu, kendi sınıfınızı yuvarlamaktan daha az taşınabilir.



0

Neden bunu denemiyorsun?

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

Çünkü A) işe yaramayabilir ve B) işe yararsa, sadece çıktıyı bastırmak yerine bir hata alırsınız.
Mad Physicist

0
sys.stdout = None

Bunun için sorun yok print()durumda. Ancak, herhangi bir sys.stdout yöntemini çağırırsanız, bir hataya neden olabilir, örn sys.stdout.write().

Dokümanlarda bir not var :

Bazı koşullar altında stdin, stdout ve stderr ile orijinal değerler stdin , stdout ve stderr None olabilir. Genellikle bir konsola bağlı olmayan Windows GUI uygulamaları ve pythonw ile başlayan Python uygulamaları için geçerlidir.


"Tamam" ve "Yazdırmaya çalıştığınız anda hata vermeyecek" bu durumda çok farklı şeylerdir.
Mad Physicist

1
Haklısın. Herhangi bir yöntemi arayamazsınız, sys.stdout = Nullbu nedenle bazı durumlarda tehlikeli olabilir.
Alexander Chzhen

Tam da bu durumda tehlikeli olduğu bilinmektedir. OP, özellikle bunu yapmaktan aldığı hatayı ele alır.
Mad Physicist

0

EKİ iFreilicht cevabı - bu hem piton 2 & 3 için çalışır.

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!")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.