Як заблокувати дзвінки для друку?


81

Чи є спосіб зупинити виклик функції print?


Я використовую pygame.joystickмодуль для гри, над якою працюю.

Я створив pygame.joystick.Joystickоб'єкт і у фактичному циклі гри викликаю його функцію-учасника, get_buttonщоб перевірити, чи вводить користувач. Функція робить все, що мені потрібно, але проблема в тому, що вона також викликає print, що значно уповільнює гру.

Чи можу я заблокувати цей дзвінок print?

Відповіді:


108

Python дозволяє перезаписувати стандартний вивід (stdout) будь-яким об'єктом файлу. Це має працювати на різних платформах і писати на нульовий пристрій.

import sys, os

# Disable
def blockPrint():
    sys.stdout = open(os.devnull, 'w')

# Restore
def enablePrint():
    sys.stdout = sys.__stdout__


print 'This will print'

blockPrint()
print "This won't"

enablePrint()
print "This will too"

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


23
Здається, це назавжди заблокувало друк для мене. enablePrint не відновлює його
Джонні V

10
Це рішення не відновить належним чином друк у клітинках Юпітера
oski86,

1
Я думаю, аргумент для друку все ще обчислюється, тож це займе більше часу, ніж сценарій із усіма рядками друку, коментованими вручну?
Радіокеровано

79

На основі рішення @FakeRainBrigand я пропоную більш безпечне рішення:

import os, sys

class HiddenPrints:
    def __enter__(self):
        self._original_stdout = sys.stdout
        sys.stdout = open(os.devnull, 'w')

    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout.close()
        sys.stdout = self._original_stdout

Тоді ви можете використовувати його так:

with HiddenPrints():
    print("This will not be printed")

print("This will be printed as before")

Це набагато безпечніше, оскільки ви не можете забути ввімкнути stdout, що особливо важливо при обробці винятків.

Без with

У наступному прикладі використовуються функції увімкнення / вимкнення друку, запропоновані у попередній відповіді.

Уявіть, що існує код, який може спричинити виняток. Нам довелося використовувати finallyоператор, щоб у будь-якому випадку дозволити друк.

try:
    disable_prints()
    something_throwing()
    enable_prints() # This will not help in case of exception
except ValueError as err:
    handle_error(err)
finally:
    enable_prints() # That's where it needs to go.

Якщо ви забули finallyпункт, жоден із ваших printдзвінків більше нічого не надрукував.

Безпечніше використовувати withоператор, який гарантує повторне ввімкнення відбитків.

Примітка: Не безпечно використовувати sys.stdout = None, тому що хтось може викликати такі методиsys.stdout.write()


6
Дуже хороше рішення! Я просто написав те саме, а потім виявив, що ви вже відповіли таким чином: D Додам трохи інформації про те, чому це кращий спосіб це зробити.
iFreilicht

Питання Noobie тут: Чи було б важливо закрити () devnull після виходу з класу?
jlsecrest

@Wadsworth, я не знаю відповіді. Я думаю, властивості devnull не вимагають його належного закриття. Але чутки кажуть, що ви завжди повинні закривати відкриті обробники, щоб звільнити ресурси. CPython, наскільки я знаю, повинен закрити себе devnull, наскільки це збирач сміття отримує.
Олександр Чжень

2
Я отримував, ResourceWarning: unclosed file <_io.TextIOWrapper name='/dev/null' mode='w' encoding='UTF-8'>використовуючи цей код, вирішив його, встановивши sys.stdout = None замість open (os.devnull, 'w')
WellDone2094,

@ WellDone2094, дякую. Я додав sys.stdout.close()до методу виходу. Це має допомогти. Зверніть увагу, що це sys.stdout = Noneможе спричинити помилку, оскільки хтось може викликати такі методи stdout, як sys.stdout.write().
Олександр Чжен

33

Як запропонував @Alexander Chzhen, використання контекстного менеджера було б безпечнішим, ніж виклик пари функцій, що змінюють стан.

Однак вам не потрібно повторно впроваджувати диспетчер контексту - він уже є у стандартній бібліотеці. Ви можете перенаправити stdout(файловий об'єкт, який printвикористовує) за допомогою contextlib.redirect_stdout, а також за stderrдопомогою contextlib.redirect_stderr.

import os
import contextlib

with open(os.devnull, "w") as f, contextlib.redirect_stdout(f):
    print("This won't be printed.")

7

Якщо ви хочете заблокувати друковані дзвінки, зроблені певною функцією, є більш акуратне рішення із використанням декораторів. Визначте такий декоратор:

# decorater used to block function printing to the console
def blockPrinting(func):
    def func_wrapper(*args, **kwargs):
        # block all printing to the console
        sys.stdout = open(os.devnull, 'w')
        # call the method in question
        value = func(*args, **kwargs)
        # enable all printing to the console
        sys.stdout = sys.__stdout__
        # pass the return value of the method back
        return value

    return func_wrapper

Тоді просто поставте @blockPrintingперед будь-якою функцією. Наприклад:

# This will print
def helloWorld():
    print("Hello World!")
helloWorld()

# This will not print
@blockPrinting
def helloWorld2():
    print("Hello World!")
helloWorld2()

2

Ні, цього немає, особливо, що більшість PyGame написана на мові C.

Але якщо ця функція викликає print, то це помилка PyGame, і вам слід просто повідомити про це.


2

У мене була та ж проблема, і я не прийшов до іншого рішення, а перенаправив результат програми (я точно не знаю, чи відбувається спам у stdout або stderr) на /dev/nullnirvana.

Дійсно, це з відкритим кодом, але я не був настільки пристрасним, щоб зануритись у pygameджерела - і процес збірки - щоб якось зупинити спам налагодження.

РЕДАГУВАТИ:

pygame.joystickМодуль має виклики на printfвсі функції , які повертають фактичні значення Python:

printf("SDL_JoystickGetButton value:%d:\n", value);

На жаль, вам доведеться це прокоментувати і перекомпілювати все. Можливо, надане setup.pyполегшить це, ніж я думав. Ви можете спробувати це ...


2

Зовсім іншим підходом було б перенаправлення в командному рядку. Якщо ви використовуєте Windows, це означає пакетний сценарій. У Linux, bash.

/full/path/to/my/game/game.py > /dev/null
C:\Full\Path\To\My\Game.exe > nul

Якщо ви не маєте справу з кількома процесами, це має спрацювати. Для користувачів Windows це можуть бути ярлики, які ви створюєте (меню «Пуск» / робочий стіл).


1

Модуль, в якому я друкував stderr. Тож рішенням у такому випадку було б:

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

1

На основі рішення @Alexander Chzhen, я представляю тут спосіб застосувати його до функції з можливістю придушити друк чи ні.

    import os, sys
    class SuppressPrints:
        #different from Alexander`s answer
        def __init__(self, suppress=True):
            self.suppress = suppress

        def __enter__(self):
            if self.suppress:
                self._original_stdout = sys.stdout
                sys.stdout = open(os.devnull, 'w')

        def __exit__(self, exc_type, exc_val, exc_tb):
            if self.suppress:
                sys.stdout.close()
                sys.stdout = self._original_stdout
    #implementation
    def foo(suppress=True):
        with SuppressPrints(suppress):
            print("It will be printed, or not")

    foo(True)  #it will not be printed
    foo(False) #it will be printed

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


0

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

enable_print  = print
disable_print = lambda *x, **y: None

print = disable_print
function_that_has_print_in_it(1)  # nothing is printed

print = enable_print
function_that_has_print_in_it(2)  # printing works again!

Примітка: це працює лише для того, щоб вимкнути функцію print (), і не вимкне весь вихід, якщо ви здійснюєте дзвінок до чогось іншого, що видає вихід. Наприклад, якщо ви викликали бібліотеку C, яка виробляла власний вивід, у stdout, або якщо ви використовували intput ().

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.