Як надрукувати на stderr в Python?


1339

Існує кілька способів написання на stderr:

# Note: this first one does not work in Python 3
print >> sys.stderr, "spam"

sys.stderr.write("spam\n")

os.write(2, b"spam\n")

from __future__ import print_function
print("spam", file=sys.stderr)

Це, здається, суперечить дзену Python # 13 , тож яка різниця тут і чи є якісь переваги чи недоліки в той чи інший спосіб? Який спосіб слід використовувати?

Має бути один - і бажано лише один - очевидний спосіб зробити це.


46
Перший перелічений спосіб - це одна з багатьох речей, видалених у Python 3. Здається, що консенсус полягає в тому, що синтаксис >> все одно був некрасивим, а оскільки друк зараз є функцією, синтаксис ніколи не буде працювати.
Стів Ховард

23
Я використовую: sys.exit ('Помилка: <текст помилки>')
старий

1
просто використовуйте друк.
PythonMaster202

Відповіді:


1165

Я виявив, що це єдиний короткий + гнучкий + портативний + читабельний:

from __future__ import print_function
import sys

def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)

Функцію eprintможна використовувати так само, як і стандартну printфункцію:

>>> print("Test")
Test
>>> eprint("Test")
Test
>>> eprint("foo", "bar", "baz", sep="---")
foo---bar---baz

29
Лише одна думка: оскільки це імпортує функцію друку, кожне інше "друк" у оригінальному сценарії тепер потрібно "функціонувати", додаючи "(" і ")". Отже, це незначний страйк проти цього методу, ІМО.
Дан Н

45
@DanH Так, це змушує вас зробити готовий код Python3. Я думаю, що це може бути причиною того, що багатьом насправді подобається!
Марч

18
@MarkH ... так, це змушує вас зробити свій код готовим до Python3 ... він також змушує вас це робити ЗАРАЗ, просто надрукувати інформацію про налагодження в stderr ... що я можу знайти більше клопоту в більшість ситуацій, коли я намагаюся щось налагодити. (Я б краще не вводив нові синтаксичні помилки!) :-)
Dan H

29
FWIW цей код не змушує вас використовувати функцію версії printу всій вашій програмі. Тільки в модулі, що містить визначення eprint(). Помістіть його у невеликий файл самостійно, імпортуйте eprintз нього в інші файли, і ви можете продовжувати використовувати операцію print, скільки завгодно.
alexis

3
Крім того, перетворення з функції друку на друк - це проста заміна пошуку, яка 2to3 вже автоматизує для вас. Просто зробіть це вже, якщо цього не зробили; python2 піде менш ніж за 2 роки ... Деякі речі між 2 і 3 можуть стати дещо складними; функція друку - не одна з них. Дивіться docs.python.org/2/library/2to3.html
bobpaul

548
import sys
sys.stderr.write()

Це мій вибір, просто більш читабельний і конкретно сказаний, що ви маєте намір зробити, і переноситься в різних версіях.

Редагувати: бути "пітонічним" - це третя думка для мене щодо читабельності та продуктивності ... з огляду на ці дві речі, якщо python 80% вашого коду буде пітонічним. розуміння списку - це «велика річ», яка використовується не так часто (читабельність).


88
Тільки не забудьте промити.
темто

10
Перевагою printоператора є легке друкування не рядкових значень, без необхідності їх попереднього перетворення. Якщо вам потрібна заява для друку, я рекомендую використовувати 3-й варіант, щоб бути готовим до python 3
vdboor

56
sys.stderr.write()нічого подібного print. Він не додає новий рядок.
Полковник Паніка

41
@temoto - stderr не буферизований, тому не потрібно промивати.
Мет

11
@SkipHuffman Ви маєте на увазі os.linesep. Про це stderrми говоримо, зрештою. Не хочете, щоб консоль псувалася з неправильною новою лінією.
jpmc26

179

print >> sys.stderr немає в Python3. http://docs.python.org/3.0/whatsnew/3.0.html говорить:

Old: print >> sys.stderr, "fatal error"
New: print("fatal error", file=sys.stderr)

Багатьом з нас здається дещо неприродно перенести призначення до кінця команди. Альтернатива

sys.stderr.write("fatal error\n")

виглядає більш об’єктно орієнтованим і елегантно переходить від загального до конкретного. Але зауважте, що writeце не заміна 1: 1 print.


5
Я гадаю, це питання переваги, але я не бачу, у чому негарна print('spam', file=sys.stderr). Якщо ви робите це знову і знову, ви можете кодувати функцію 'eprint', як у найпопулярнішій відповіді, але в такому випадку я запитав би, що не так із реєстрацією? stackoverflow.com/a/41304513/1450294
Майкл Шепер

Ще одним способом уточнення наміру було б зробити with sys.stderr as dest:перед відступним дзвінком доprint("ERROR", file=dest)
MarkHu

130

Ніхто loggingще не згадував , але журнал був створений спеціально для передачі повідомлень про помилки. Основна конфігурація встановить запис обробника потоку в stderr.

Цей сценарій:

# foo.py
import logging

logging.basicConfig(format='%(message)s')
log = logging.getLogger(__name__)
log.warning('I print to stderr by default')
print('hello world')

має такий результат при запуску в командному рядку:

$ python3 foo.py > bar.txt
I print to stderr by default

і bar.txt буде містити "привіт світ", надрукований на stdout.


2
На мій досвід, більше людей використовують друк для журналу повідомлень, ніж використовують журнал. Я думаю, що python4 повинен просто видалити друк з мови та змусити вас використовувати для цього журнал.
Mnebuerquo

1
Це найкраща відповідь !! ... Я боровся з друком або sys або хто знає ... коли потрібна правильна реєстрація ... дякую за гарну ідею
Карлос

129

Для Python 2 мій вибір такий: print >> sys.stderr, 'spam' Тому що ви можете просто надрукувати списки / диски тощо, не перетворюючи їх у рядок. print >> sys.stderr, {'spam': 'spam'} замість: sys.stderr.write(str({'spam': 'spam'}))


6
Більш пітонічним способом друкувати словник був би "{0}".format({'spam': 'spam'})все-таки щось подібне , чи не так? Я б сказав, що вам слід уникати явного перетворення на рядок. Редагувати: Я випадково граматика
luketparkinson

1
@luketparkinson це все про налагодження - тому, я думаю, переважніше використовувати найпростіший код, наскільки це можливо.
Франківський Богдан

88
Це не працює на Python 3, тому вам слід уникати цього в новому коді.
JonnyJD

33

Я зробив наступне за допомогою Python 3:

from sys import stderr

def print_err(*args, **kwargs):
    print(*args, file=stderr, **kwargs)

Тому тепер я можу додати аргументи ключових слів, наприклад, щоб уникнути повернення перевезення:

print_err("Error: end of the file reached. The word ", end='')
print_err(word, "was not found")

2
Я збирався запропонувати вам також використовувати часткове, але зрозумів, що частковий призначає stderr функції під час створення часткової. Це запобігає перенаправлення stderr пізніше, оскільки часткова частина все ще буде містити початковий об'єкт stderr.
Ребс

31

Я б сказав, що ваш перший підхід:

print >> sys.stderr, 'spam' 

це "Один. Очевидний спосіб зробити це". Інші не задовольняють правилу №1 ("Красиве краще, ніж потворне".)


109
Думки різняться. Для мене це найменш очевидно .
porgarmingduod

4
@AliVeli Немає дужок, це старший синтаксис Python <= 2, а тому не сумісний з Python 3.
Ребс

30
Я б сказав , що це сама потворна версія всіх 3
вулкан

4
Що це >>означає синтаксично? Я розумію, що це копія баш-файлів >, тому чи потрібно це зробити якийсь синтаксис, що вчинив обурення?
Дмитро Сіренко

2
@EarlGray Це передача від оператора вставки потоку C ++:std::cout << "spam";
Шон Алред

19

Це буде імітувати стандартну функцію друку, але виводити на stderr

def print_err(*args):
    sys.stderr.write(' '.join(map(str,args)) + '\n')

8
Я додав би sys.stderr.flush ()
AMS

4
@AMS - Чому? printне включає флеш.
Robᵩ

4
Навіщо наслідувати, коли ти насправді можеш це зробити?
Божевільний фізик

19

У Python 3 можна просто використовувати print ():

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

майже поза коробкою:

import sys
print("Hello, world!", file=sys.stderr)

або:

from sys import stderr
print("Hello, world!", file=stderr)

Це прямо і не потрібно нічого включати sys.stderr.


16

EDIT В огляді я думаю, що потенційна плутанина із зміною sys.stderr та не бачення поведінки оновленою робить цю відповідь не такою вдалою, як просто використання простої функції, як вказували інші.

Використання часткового зберігає лише 1 рядок коду. Потенційна плутанина не варто зберігати 1 рядок коду.

оригінальний

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

from __future__ import print_function
import sys
from functools import partial

error = partial(print, file=sys.stderr)

Потім ви використовуєте його так

error('An error occured!')

Ви можете перевірити, чи друкується на stderr, а не stdout, виконавши наступне (код надмірної версії від http://coreygoldberg.blogspot.com.au/2009/05/python-redirect-or-turn-off-stdout-and .html ):

# over-ride stderr to prove that this function works.
class NullDevice():
    def write(self, s):
        pass
sys.stderr = NullDevice()

# we must import print error AFTER we've removed the null device because
# it has been assigned and will not be re-evaluated.
# assume error function is in print_error.py
from print_error import error

# no message should be printed
error("You won't see this error!")

Мінусом цього є часткове присвоєння функції sys.stderr оберненій функції під час створення. Що означає, якщо пізніше перенаправити stderr, це не вплине на цю функцію. Якщо ви плануєте перенаправляти stderr, то використовуйте метод ** kwargs, згаданий aaguirre на цій сторінці.


Чи найкраще працює код Корі Голдберг на машині Rube Goldberg? : P
Agi Hammerthief

1
BTW: "currying" - це (більше) корисне ключове слово, якщо ви хочете дізнатися більше про "часткове".
Марч

5

Те саме стосується stdout:

print 'spam'
sys.stdout.write('spam\n')

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

  1. Пізніше ви можете вирішити переключитися між stdout / stderr та звичайним файлом.

  2. синтаксис print () змінився в Python 3, тому, якщо вам потрібно підтримати обидві версії, write () може бути краще.


4
Використання from __future__ import print_function- це кращий спосіб підтримати і Python 2.6+, і Python 3.
Phoenix

4

Я працюю в python 3.4.3. Я вирізаю невеликий текст, який показує, як я потрапив сюди:

[18:19 jsilverman@JSILVERMAN-LT7 pexpect]$ python3
>>> import sys
>>> print("testing", file=sys.stderr)
testing
>>>
[18:19 jsilverman@JSILVERMAN-LT7 pexpect]$ 

Це спрацювало? Спробуйте перенаправити stderr на файл і подивіться, що станеться:

[18:22 jsilverman@JSILVERMAN-LT7 pexpect]$ python3 2> /tmp/test.txt
>>> import sys
>>> print("testing", file=sys.stderr)
>>> [18:22 jsilverman@JSILVERMAN-LT7 pexpect]$
[18:22 jsilverman@JSILVERMAN-LT7 pexpect]$ cat /tmp/test.txt
Python 3.4.3 (default, May  5 2015, 17:58:45)
[GCC 4.9.2] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
testing

[18:22 jsilverman@JSILVERMAN-LT7 pexpect]$

Ну, окрім того, що маленьке введення, яке дає вам питон, було розбито на stderr (куди б інакше він пішов?), Воно працює.


2

Якщо ви робите простий тест:

import time
import sys

def run1(runs):
    x = 0
    cur = time.time()
    while x < runs:
        x += 1
        print >> sys.stderr, 'X'
    elapsed = (time.time()-cur)
    return elapsed

def run2(runs):
    x = 0
    cur = time.time()
    while x < runs:
        x += 1
        sys.stderr.write('X\n')
        sys.stderr.flush()
    elapsed = (time.time()-cur)
    return elapsed

def compare(runs):
    sum1, sum2 = 0, 0
    x = 0
    while x < runs:
        x += 1
        sum1 += run1(runs)
        sum2 += run2(runs)
    return sum1, sum2

if __name__ == '__main__':
    s1, s2 = compare(1000)
    print "Using (print >> sys.stderr, 'X'): %s" %(s1)
    print "Using (sys.stderr.write('X'),sys.stderr.flush()):%s" %(s2)
    print "Ratio: %f" %(float(s1) / float(s2))

Ви побачите, що sys.stderr.write () стабільно в 1,81 рази швидше!


Якщо я запускаю це, я бачу набагато меншу різницю. Цікаво, що більшість відповідей ігнорує функцію друку (python 3) способом. Я ніколи не використовував його раніше (за інерцією), але думав, що запускаю цей сценарій синхронізації та додаю функцію друку. Безпосереднє порівняння оператора друку та функції друку неможливо (імпорт з майбутнього стосується всього файлу та маскує оператор друку), але переписавши цей код для використання функції друку замість оператора, я бачу більший прискорення (~ 1.6, хоча дещо мінливий ) на користь функції друку.
Гаміш

1
Результат цього тесту якимось чином вводить в оману. Надрукуйте "XXXXXXXXXXXXXXXXXXXX" замість "X", а співвідношення впаде до 1,05 . Я припускаю, що більшості програм python потрібно надрукувати більше, ніж один символ.
Завжди просив

14
Мені не хвилюється продуктивність, бо щось подібне до друку попереджень.
wim

Я знаю, що минув час, але ви відповіли однаково довго після моєї посади ... Якщо ви не поцікавитеся продуктивністю, ніж я б запропонував, більш пітонічним способом було б використовувати sys.stderr.write, а не WTF?!? ">>" символів. Якщо цей простір імен sys.stdout занадто довгий, ви можете перейменувати його ... (тобто з sys import stderr як stderr_fh). Тоді ви можете зробити stderr_fh.write ("бла")
ThePracticalOne

1
[3/3] Навіть якщо цей показник був би більш точним, його, мабуть, не варто турбувати. Як писав Кнут: "Програмісти витрачають величезну кількість часу на роздуми або занепокоєння швидкості некритичних частин своїх програм, і ці спроби ефективності насправді мають сильний негативний вплив при розгляді налагодження та обслуговування. Ми повинні забути про малі ефективність, говорять про 97% часу: передчасна оптимізація - корінь усього зла ».
Корі Голдберг

1

Якщо ви хочете вийти з програми через фатальну помилку, скористайтеся:

sys.exit("Your program caused a fatal error. ... description ...")

і import sysв заголовку.


-2

Відповідь на питання: Існує різний спосіб друкувати stderr в python, але це залежить від 1.) яку версію python ми використовуємо 2.) який точний вихід ми хочемо.

Різниця між функцією запису друку та stderr: stderr : stderr (стандартна помилка) - це труба, яка вбудовується в кожну систему UNIX / Linux, коли ваша програма виходить з ладу і виводить інформацію про налагодження (як трекбек у Python), вона переходить до stderr труба.

print : print - це обгортка, яка форматує вхідні дані (вхід - це пробіл між аргументом та новим рядком в кінці), а потім викликає функцію запису заданого об'єкта, даний об'єкт за замовчуванням - sys.stdout, але ми можемо передайте файл, тобто ми також можемо надрукувати вхід у файл.

Python2: Якщо ми використовуємо python2, то

>>> import sys
>>> print "hi"
hi
>>> print("hi")
hi
>>> print >> sys.stderr.write("hi")
hi

Кінцева кома Python2 має в Python3 стати параметром, тому якщо ми використовуємо трейлісні коми, щоб уникнути нового рядка після друку, це в Python3 буде виглядати як print ('Текст для друку', end = ''), що є синтаксичною помилкою під Python2 .

http://python3porting.com/noconv.html

Якщо ми перевіримо той же сценарій вище в python3:

>>> import sys
>>> print("hi")
hi

Під Python 2.6 є майбутній імпорт, щоб зробити друк у функції. Отже, щоб уникнути помилок синтаксису та інших відмінностей, ми повинні запустити будь-який файл, у якому ми використовуємо print () з майбутнього імпорту print_function. Майбутнє імпорту працює тільки в Python 2.6 і пізніше, тому для Python 2.5 і вище у вас є два варіанти. Ви можете або перетворити більш складний друк на щось простіше, або ви можете використовувати окрему функцію друку, яка працює як під Python2, так і з Python3.

>>> from __future__ import print_function
>>> 
>>> def printex(*args, **kwargs):
...     print(*args, file=sys.stderr, **kwargs)
... 
>>> printex("hii")
hii
>>>

Випадок: зауважте, що sys.stderr.write () або sys.stdout.write () (stdout (стандартний вихід) - це труба, яка вбудована в кожну систему UNIX / Linux) - це не заміна друку, але так ми можемо використовувати його як альтернативу в деяких випадках. Друк - це обгортка, яка обертає вхід пробілом та новою лінією в кінці та використовує функцію запису для запису. Це причина sys.stderr.write () швидше.

Примітка: ми також можемо відстежувати та налагоджувати за допомогою журналу

#test.py
import logging
logging.info('This is the existing protocol.')
FORMAT = "%(asctime)-15s %(clientip)s %(user)-8s %(message)s"
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logging.warning("Protocol problem: %s", "connection reset", extra=d)

https://docs.python.org/2/library/logging.html#logger-objects

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