Подтвердить что ты не робот

Одновременная печать на экран и запись в файл

Я нашел некоторый код в Интернете, который обычно работает, но я хочу использовать его несколько раз в одной и той же программе (писать разные вещи в разные файлы, все время печатая на экране все время).

То есть, когда он закрывается, я думаю, что sys.stdout закрывается, поэтому печать вообще, и использование этого класса снова терпит неудачу. Я пробовал реимпортировать sys и другие глупые вещи, но я не могу заставить его работать.

Здесь сайт и код groups.google.com/group/comp.lang.python/browse_thread/thread/d25a9f5608e473af/

import sys

class MyWriter:

    def __init__(self, stdout, filename):
        self.stdout = stdout
        self.logfile = file(filename, 'a')

    def write(self, text):
        self.stdout.write(text)
        self.logfile.write(text)

    def close(self):
        self.stdout.close()
        self.logfile.close()

writer = MyWriter(sys.stdout, 'log.txt')
sys.stdout = writer

print 'test' 
4b9b3361

Ответ 1

Вы пытаетесь воспроизвести плохо что-то, что хорошо сделано в стандартной библиотеке Python; пожалуйста, проверьте модуль протоколирования.

С помощью этого модуля вы можете делать именно то, что хотите, но гораздо проще, стандартно и расширяемо. Вы можете действовать следующим образом (этот пример является копией/вставкой из журнальной книги):

Предположим, вы хотите войти в консоль и файл с другим сообщением форматов и в разных обстоятельствах. Предположим, вы хотите регистрировать сообщения с уровнями DEBUG и выше в файл, а те сообщения на уровне INFO и выше на консоль. Предположим также, что файл должен содержат временные метки, но сообщения консоли не должны. Вот как вы можете достичь этого:

import logging

# set up logging to file - see previous section for more details
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    datefmt='%m-%d %H:%M',
                    filename='/temp/myapp.log',
                    filemode='w')
# define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
# set a format which is simpler for console use
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
# tell the handler to use this format
console.setFormatter(formatter)
# add the handler to the root logger
logging.getLogger('').addHandler(console)

# Now, we can log to the root logger, or any other logger. First the root...
logging.info('Jackdaws love my big sphinx of quartz.')

# Now, define a couple of other loggers which might represent areas in your
# application:

logger1 = logging.getLogger('myapp.area1')
logger2 = logging.getLogger('myapp.area2')

logger1.debug('Quick zephyrs blow, vexing daft Jim.')
logger1.info('How quickly daft jumping zebras vex.')
logger2.warning('Jail zesty vixen who grabbed pay from quack.')
logger2.error('The five boxing wizards jump quickly.')

Когда вы запустите это, на консоли вы увидите

root        : INFO     Jackdaws love my big sphinx of quartz.
myapp.area1 : INFO     How quickly daft jumping zebras vex.
myapp.area2 : WARNING  Jail zesty vixen who grabbed pay from quack.
myapp.area2 : ERROR    The five boxing wizards jump quickly.

и в файле вы увидите что-то вроде

10-22 22:19 root         INFO     Jackdaws love my big sphinx of quartz.
10-22 22:19 myapp.area1  DEBUG    Quick zephyrs blow, vexing daft Jim.
10-22 22:19 myapp.area1  INFO     How quickly daft jumping zebras vex.
10-22 22:19 myapp.area2  WARNING  Jail zesty vixen who grabbed pay from quack.
10-22 22:19 myapp.area2  ERROR    The five boxing wizards jump quickly.

Как вы можете видеть, сообщение DEBUG отображается только в файле. Другой сообщения отправляются в оба пункта назначения.

В этом примере используются консоли и обработчики файлов, но вы можете использовать любые количество и комбинацию выбранных вами обработчиков.

Ответ 2

Easy-peasy с Python 3.3 и выше

Начиная с Python 3.3, сделать это стало значительно проще, так как logging.basicConfig теперь принимает аргумент handlers =.

import logging

level = logging.INFO
format = '  %(message)s'
handlers = [logging.FileHandler('filename.log'), logging.StreamHandler()]
logging.basicConfig(level = level, format = format, handlers = handlers)

logging.info('Hey, this is working!')

Обратите внимание, что некоторые модули Python также могут размещать сообщения журнала на уровне INFO.

Здесь удобно создать пользовательский уровень ведения журнала, например, OK, на 5 уровней выше значения по умолчанию INFO и 5 уровней ниже уровня WARNING по умолчанию.

Ответ 3

Удалите строку, которая делает то, что вы явно говорите, что вы не хотите делать: первая строка close(), которая закрывает stdout.

Ответ 4

То есть, когда он закрывается, я думаю, sys.stdout закрывается, поэтому печать на всех, и использование этого класса снова терпит неудачу. Я попытался переименовать sys и другие глупые вещи, но я не могу заставить его работать.

Чтобы ответить на ваш вопрос, вы не должны закрывать stdout. Интерпретатор python открывает stdout, stdin и stderror при запуске. Для того, чтобы печать работала, интерпретатору требуется, чтобы stdout был открыт. Reimporting sys ничего не делает, как только модуль загружен. Вам нужно будет перезагрузить модуль. В этом конкретном случае я не уверен, что перезагрузка устранит проблему, поскольку sys.stdout позволяет stdout использоваться в качестве файлового объекта.

Кроме того, я думаю, что у вас есть ошибка в вашем коде, которая может вызвать печать ломать. В строке 2 вы назначаете объект MyWriter для sys.stdout. Это может быть закрыто stdout, когда сборщик мусора удаляет неиспользуемый файл stdout.

writer = MyWriter(sys.stdout, 'log.txt')
sys.stdout = writer