Перенаправлення stdout на "нічого" в python


128

У мене великий проект, що складається з достатньо великої кількості модулів, кожен з яких друкує щось до стандартного виходу. Зараз, коли проект збільшився, великих немає. зprint заяв друку багато на станд відмови , який зробив програму значно повільніше.

Отже, тепер я хочу , щоб вирішити , на час виконання або не друкувати нічого на стандартний висновок. Я не можу вносити зміни в модулі, оскільки їх досить багато. (Я знаю, що можу перенаправити stdout у файл, але навіть це значно повільно.)

Отже, моє запитання полягає в тому, як я перенаправляю stdout на нічого, тобто як змусити 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.

В даний час єдина у мене ідея - створити клас, який має метод запису (який нічого не робить) і перенаправити stdout до екземпляра цього класу.

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

dp = DontPrint()
sys.stdout = dp

Чи є вбудований механізм у python для цього? Або є щось краще, ніж це?



1
Я щойно відкрив своєрідну річ. Перетворення sys.stdout у None насправді не працює в моїй програмі, хоча я не знаю чому. У мене були справді дивні проблеми з перекодіровкою кодування на os.devnull, тому я спробував просто використовувати None, і це працює. Можливо, щось стосується мене, коли я роблю це в тесті Джанго, але я не впевнений.
Тікін

Відповіді:


233

Міжплатформа:

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

Як ви скасуєте це? Чи є спосіб повернути заяви про друк після того, як ви закінчите?
jj172

4
Звичайно, просто збережіть посилання на оригінал sys.stdoutі поверніть його після. Наприклад, old_stdout = sys.stdoutперед будь-яким іншим кодом, потім, sys.stdout = old_stdoutколи ви закінчите.
Ендрю Кларк

1
Примітка: це не пересилає на рівні , тобто дескриптор файлу, він може не перенаправити висновок зовнішніх дочірніх процесів, виведення з розширень C , що друк на C стандартний висновок безпосередньо os.write(1, b'abc\n'). Потрібно os.dup2(), щоб перенаправити на рівень дескриптора файлів, див.stdout_redirected()
jfs

4
Зверніть увагу, що sys.__stdout__для скасування переадресації існує. Таким чином, ви просто робите sys.stdout = sys.__stdout__і не потрібно зберігати резервну копію.
Костянтин Секереш

28

Гарний спосіб зробити це - створити невеликий контекстний процесор, в який ви загортаєте свої відбитки. Потім ви просто використовуєте його з withтвердженням, щоб заглушити весь вихід.

Пітон 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")

Запуск цього коду друкує лише другий рядок виводу, а не перший:

$ python test.py
this will print

Це працює на платформі (Windows + Linux + Mac OSX) і чистіше, ніж ті, хто відповідає на них.


@celticminstrel ви абсолютно праві, дякую, що виправили мене. Я оновив код, щоб потім скинути sys.stdout.
Еміль Стенстрем

Тут не вистачає перевірки помилок, але чудова ідея
Mad Physicist

Додано код, щоб показати, як це працює і в Python 3.
Еміль Стенстрем

15

Якщо ви знаходитесь у python 3.4 або новішої версії, є просте та безпечне рішення за допомогою стандартної бібліотеки:

import contextlib

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

3
with contextlib.redirect_stdout(None)працює також
Кріс Когдон

@ChrisCogdon Дуже хороший натяк, я відповідним чином відредагував відповідь.
iFreilicht

1
Не працює, якщо ви використовуєте бібліотеку pprint. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy

@Speeeddy Ви отримали цю помилку з python 2? Ви можете створити клас writeметодом манекена , який нічого не робить. Дивіться мою відповідь нижче.
dizcza

8

(принаймні в моїй системі) виявляється, що запис у os.devnull приблизно в 5 разів швидший, ніж запис до класу DontPrint, тобто

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

дав такий вихід:

<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
Просто для запису, в наступний раз , коли ви хочете що - то час, просто використовувати timeit
Antoine ротонду

6

Якщо ви перебуваєте в середовищі Unix (включений Linux), ви можете перенаправити вихід на /dev/null:

python myprogram.py > /dev/null

А для Windows:

python myprogram.py > nul

2
Краще мати рішення, яке б працювало на всіх платформах.
Пушпак Даґада

2
@Guanidene, можливо, але якщо він є єдиною людиною, яка використовує свою програму, він може значною мірою гарантувати навколишнє середовище.
Нік Радфорд

nul = myfunc() ?
Hicsy

2

Як щодо цього:

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

Це використовує функції модуля контекстної ліги, щоб приховати вихід будь-якої команди, яку ви намагаєтеся запустити, залежно від результату should_hide_output(), а потім відновить поведінку виводу після виконання цієї функції.

Якщо ви хочете приховати стандартний вихід помилок, імпортуйте redirect_stderrз нього contextlibта додайте вислів рядка stack.enter_context(redirect_stderr(null_stream)).

Основний мінус у тому, що це працює лише в Python 3.4 та пізніших версіях.



1

Ваш клас буде працювати чудово (за винятком назви write()методу - його потрібно викликати write(), малі літери). Просто переконайтеся, що ви зберегли копію sys.stdoutв іншій змінній.

Якщо ви перебуваєте на * NIX, ви можете це зробити sys.stdout = open('/dev/null'), але це менш портативно, ніж прокатка власного класу.




0
sys.stdout = None

Це добре для print()випадку. Але це може призвести до помилки, якщо ви викликаєте будь-який метод sys.stdout, наприклад sys.stdout.write().

У документах є примітка :

За деяких умов stdin, stdout та stderr, а також початкові значення stdin , stdout та stderr можуть бути None. Зазвичай це стосується програм GUI для Windows, які не підключені до консолі, а додатки Python запущені з pythonw.


"ОК" і "Не викине помилку в момент, коли ви спробуєте надрукувати", в цьому випадку дуже різні речі.
Божевільний фізик

1
Ти маєш рацію. Ви не можете викликати будь-який метод, sys.stdout = Nullтому в деяких випадках це може бути небезпечно.
Олександр Чжен

У цьому точному випадку, як відомо, це небезпечно. ОП спеціально вирішує помилку, яку він отримує від цього.
Божевільний фізик

0

Доповнення до відповіді 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!")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.