Bagaimana cara mencetak ke stderr dengan Python?

1340

Ada beberapa cara untuk menulis ke 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)

Itu tampaknya bertentangan dengan zen dari Python # 13 , jadi apa bedanya di sini dan apakah ada kelebihan atau kekurangan untuk satu cara atau yang lain? Jalan mana yang harus digunakan?

Seharusnya ada satu - dan lebih disukai hanya satu - cara yang jelas untuk melakukannya.

wim
sumber
46
Cara pertama yang terdaftar adalah salah satu dari banyak hal yang dihapus dengan Python 3. Konsensus tampaknya bahwa sintaks >> jelek, dan karena cetak sekarang berfungsi, sintaksis tidak akan pernah berfungsi.
Steve Howard
24
Saya menggunakan: sys.exit ('Error: <error text>')
stark
1
cukup gunakan cetak.
PythonMaster202

Jawaban:

1165

Saya menemukan ini menjadi satu-satunya yang pendek + fleksibel + portabel + dapat dibaca:

from __future__ import print_function
import sys

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

Fungsi eprintini dapat digunakan dengan cara yang sama seperti printfungsi standar :

>>> print("Test")
Test
>>> eprint("Test")
Test
>>> eprint("foo", "bar", "baz", sep="---")
foo---bar---baz
Maret
sumber
29
Hanya satu pemikiran: karena ini akan mengimpor fungsi cetak, setiap "cetak" lain dalam naskah asli sekarang perlu "difungsikan" menambahkan "(" dan ")". Jadi itu adalah sedikit serangan terhadap metode ini, IMO.
Dan H
46
@DanH Ya ini memaksa Anda untuk membuat kode Anda siap Python3. Saya kira ini bisa jadi mengapa banyak orang benar-benar menyukainya!
MarcH
18
@ MarkH ... ya, itu memaksa Anda untuk membuat kode Anda siap Python3 ... itu juga memaksa Anda untuk melakukannya SEKARANG, hanya untuk mencetak beberapa info debug ke stderr ... yang saya akan menemukan lebih banyak kerumitan dalam kebanyakan situasi ketika saya mencoba men-debug sesuatu. (Saya lebih suka tidak memperkenalkan kesalahan sintaksis baru!) :-)
Dan H
30
FWIW kode ini tidak memaksa Anda untuk menggunakan versi fungsi printdi seluruh program Anda. Hanya dalam modul yang berisi definisi eprint(). Masukkan ke dalam file kecil dengan sendirinya, impor eprintdari itu ke file Anda yang lain, dan Anda dapat terus menggunakan pernyataan printselama yang Anda inginkan.
alexis
4
Anda juga mengonversi dari fungsi cetak ke cetak adalah penemuan sederhana yang menggantikan 2to3 yang sudah terotomatisasi untuk Anda. Lakukan saja jika Anda belum; python2 akan hilang dalam waktu kurang dari 2 tahun ... Beberapa hal antara 2 dan 3 bisa menjadi agak rumit; fungsi cetak bukan salah satunya. Lihat docs.python.org/2/library/2to3.html
bobpaul
548
import sys
sys.stderr.write()

Adalah pilihan saya, hanya lebih mudah dibaca dan mengatakan dengan tepat apa yang ingin Anda lakukan dan portable di seluruh versi.

Sunting: menjadi 'pythonic' adalah pemikiran ketiga untuk saya tentang keterbacaan dan kinerja ... dengan dua hal ini dalam pikiran, dengan python 80% dari kode Anda akan pythonic. daftar pemahaman menjadi 'hal besar' yang tidak sering digunakan (keterbacaan).

Mike Ramirez
sumber
88
Hanya saja jangan lupa menyiram.
temoto
10
Keuntungan dari printpernyataan ini adalah mencetak nilai-nilai non-string dengan mudah, tanpa harus mengubahnya terlebih dahulu. Jika Anda memerlukan pernyataan cetak, oleh karena itu saya akan merekomendasikan menggunakan opsi ke-3 untuk menjadi python 3 siap
vdboor
56
sys.stderr.write()tidak seperti print. Itu tidak menambahkan baris baru.
Kolonel Panic
41
@temoto - stderr tidak di-buffer, jadi tidak perlu mem-flush.
Mat
11
@ SkipHuffman Maksud Anda os.linesep. Ini yang sedang stderrkita bicarakan. Tidak ingin konsol mengacaukan baris baru yang salah.
jpmc26
182

print >> sys.stderrhilang dalam Python3. http://docs.python.org/3.0/whatsnew/3.0.html mengatakan:

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

Bagi banyak dari kita, rasanya agak tidak wajar untuk memindahkan tujuan ke akhir perintah. Alternatifnya

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

terlihat lebih berorientasi objek, dan secara elegan beralih dari generik ke spesifik. Tetapi perhatikan bahwa writeitu bukan pengganti 1: 1 untuk print.

Joachim W
sumber
6
Saya kira ini masalah pilihan, tetapi saya tidak melihat apa yang buruk tentang itu print('spam', file=sys.stderr). Jika Anda melakukannya berulang-ulang, Anda dapat membuat kode fungsi 'eprint' seperti pada jawaban paling populer, tetapi dalam hal ini, saya akan bertanya, apa yang salah dengan logging? stackoverflow.com/a/41304513/1450294
Michael Scheper
1
Cara lain untuk mengklarifikasi niat adalah dengan melakukan with sys.stderr as dest:sebelum panggilan indentasi keprint("ERROR", file=dest)
MarkHu
131

Belum ada yang disebutkan logging, tetapi pencatatan dibuat khusus untuk mengkomunikasikan pesan kesalahan. Konfigurasi dasar akan mengatur penulisan stream handler ke stderr.

Skrip ini:

# foo.py
import logging

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

memiliki hasil berikut ketika dijalankan di baris perintah:

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

dan bar.txt akan berisi 'hello world' yang dicetak di stdout.

cair
sumber
3
Dalam pengalaman saya, lebih banyak orang menggunakan cetak untuk mencatat pesan daripada menggunakan pencatatan. Saya pikir python4 seharusnya hanya menghapus cetak dari bahasa dan memaksa Anda untuk menggunakan logging untuk itu.
Mnebuerquo
1
Ini jawaban terbaik !! ... Saya berjuang dengan cetak atau sys atau siapa yang tahu ... ketika penebangan yang tepat diperlukan ... terima kasih atas ide bagusnya
Carlos Saltos
129

Untuk Python 2 pilihan saya adalah: print >> sys.stderr, 'spam' Karena Anda cukup mencetak daftar / dicts dll tanpa mengubahnya menjadi string. print >> sys.stderr, {'spam': 'spam'} dari pada: sys.stderr.write(str({'spam': 'spam'}))

Frankovskyi Bogdan
sumber
6
Cara yang lebih Pythonic untuk mencetak kamus adalah dengan sesuatu seperti "{0}".format({'spam': 'spam'}), bukan? Saya akan mengatakan Anda harus menghindari konversi secara eksplisit ke string. Sunting: Saya tidak sengaja tata bahasa
luketparkinson
2
@luketparkinson ini tentang debugging - jadi, saya pikir, lebih baik menggunakan kode sesederhana mungkin.
Frankovskyi Bogdan
88
Ini tidak berfungsi pada Python 3, jadi Anda harus menghindarinya dalam kode baru.
JonnyJD
33

Saya melakukan yang berikut ini dengan menggunakan Python 3:

from sys import stderr

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

Jadi sekarang saya dapat menambahkan argumen kata kunci, misalnya, untuk menghindari carriage return:

print_err("Error: end of the file reached. The word ", end='')
print_err(word, "was not found")
aaguirre
sumber
2
Saya akan menyarankan bahwa Anda dapat menggunakan parsial juga, tetapi menyadari bahwa parsial menetapkan stderr ke fungsi pada saat pembuatan parsial. Ini mencegah Anda mengarahkan ulang stderr nanti karena sebagian masih akan memegang objek stderr asli.
Rebs
31

Saya akan mengatakan bahwa pendekatan pertama Anda:

print >> sys.stderr, 'spam' 

adalah "Satu ... cara yang jelas untuk melakukannya" Yang lain tidak memenuhi aturan # 1 ("Cantik lebih baik daripada yang jelek.")

Carl F.
sumber
109
Pendapat berbeda. Ini yang paling tidak jelas bagi saya.
porgarmingduod
4
@AliVeli Tidak ada tanda kurung, ini adalah sintaks Python <= 2 yang lebih lama, dan karenanya tidak kompatibel dengan Python 3.
Rebs
30
Saya akan mengatakan bahwa ini adalah versi paling buruk dari semua 3
gunung berapi
4
Apa >>artinya secara sintaksis? Saya mengerti bahwa ini adalah upaya untuk menyalin bash >, jadi apakah itu beberapa sintaksis shoehorned untuk melakukan hal itu?
Dmytro Sirenko
2
@EarlGray Ini adalah peninggalan dari operator penyisipan arus C ++:std::cout << "spam";
Sean Allred
19

Ini akan meniru fungsi cetak standar tetapi menghasilkan pada stderr

def print_err(*args):
    sys.stderr.write(' '.join(map(str,args)) + '\n')
Brian W.
sumber
8
Saya akan menambahkan sys.stderr.flush ()
AMS
5
@ AMS - Kenapa? printtidak termasuk flush.
Robᵩ
4
Mengapa meniru ketika Anda benar-benar bisa melakukannya?
Fisikawan Gila
19

Dalam Python 3, kita cukup menggunakan print ():

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

hampir keluar dari kotak:

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

atau:

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

Ini mudah dan tidak perlu memasukkan apa pun selain itu sys.stderr.

Florian Castellane
sumber
16

EDIT Di belakang-penglihatan, saya pikir potensi kebingungan dengan mengubah sys.stderr dan tidak melihat perilaku diperbarui membuat jawaban ini tidak sebagus hanya menggunakan fungsi sederhana seperti yang orang lain tunjukkan.

Menggunakan sebagian hanya menghemat 1 baris kode. Potensi kebingungan tidak layak disimpan 1 baris kode.

asli

Untuk membuatnya lebih mudah, inilah versi yang menggunakan 'parsial', yang merupakan bantuan besar dalam fungsi pembungkus.

from __future__ import print_function
import sys
from functools import partial

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

Anda kemudian menggunakannya seperti itu

error('An error occured!')

Anda dapat memeriksa apakah itu mencetak ke stderr dan tidak stdout dengan melakukan hal berikut (kode over-riding dari 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!")

Kelemahan dari ini adalah sebagian memberikan nilai sys.stderr ke fungsi yang dibungkus pada saat penciptaan. Yang berarti, jika Anda mengarahkan ulang stderr nanti tidak akan memengaruhi fungsi ini. Jika Anda berencana untuk mengarahkan ulang stderr, maka gunakan metode ** kwargs yang disebutkan oleh aaguirre di halaman ini.

Rebs
sumber
Apakah kode Corey Goldberg berjalan dengan baik pada mesin Rube Goldberg? : P
Agi Hammerthief
2
BTW: "currying" adalah kata kunci pencarian yang berguna (lebih banyak) jika Anda ingin tahu lebih banyak tentang "partial".
MarcH
5

Hal yang sama berlaku untuk stdout:

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

Seperti yang dinyatakan dalam jawaban lain, cetak menawarkan antarmuka yang cantik yang seringkali lebih nyaman (misalnya untuk mencetak informasi debug), sementara menulis lebih cepat dan juga bisa lebih nyaman ketika Anda harus memformat output dengan cara tertentu. Saya akan mempertimbangkan rawatan:

  1. Anda nanti dapat memutuskan untuk beralih antara stdout / stderr dan file biasa.

  2. print () sintaks telah berubah di Python 3, jadi jika Anda perlu mendukung kedua versi, tulis () mungkin lebih baik.

Seppo Enarvi
sumber
4
Menggunakan from __future__ import print_functionadalah cara yang lebih baik untuk mendukung Python 2.6+ dan Python 3.
phoenix
4

Saya bekerja di python 3.4.3. Saya memotong sedikit pengetikan yang menunjukkan bagaimana saya sampai di sini:

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

Apa itu bekerja? Coba redirect stderr ke file dan lihat apa yang terjadi:

[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]$

Nah, selain dari fakta bahwa sedikit pengantar yang diberikan python kepada Anda telah disedot ke dalam stderr (ke mana lagi ia akan pergi?), Ia bekerja.

pengguna1928764
sumber
2

Jika Anda melakukan tes sederhana:

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

Anda akan menemukan bahwa sys.stderr.write () secara konsisten 1,81 kali lebih cepat!

ThePracticalOne
sumber
Jika saya menjalankan ini saya melihat perbedaan yang jauh lebih kecil. Sangat menarik bahwa sebagian besar jawaban mengabaikan cara fungsi cetak (python 3). Saya belum pernah menggunakannya sebelumnya (inersia), tetapi saya pikir saya akan menjalankan skrip pengaturan waktu ini dan menambahkan fungsi cetak. Perbandingan langsung dari pernyataan dan fungsi cetak tidak dimungkinkan (impor dari masa depan berlaku untuk seluruh file dan menutupi pernyataan cetak) tetapi menulis ulang kode ini untuk menggunakan fungsi cetak alih-alih pernyataan yang saya lihat mempercepat lebih besar (~ 1,6 meskipun agak variabel ) mendukung fungsi cetak.
hamish
1
Hasil tes ini entah bagaimana menyesatkan. Cetak 'XXXXXXXXXXXXXXXXXXXX' alih-alih 'X' dan rasionya turun menjadi 1,05 . Saya berasumsi sebagian besar program python perlu mencetak lebih dari satu karakter.
Selalu Bertanya
15
Saya tidak peduli dengan kinerja, untuk sesuatu seperti peringatan pencetakan.
wim
Saya tahu ini sudah lama, tetapi Anda menjawab sama lama setelah posting saya ... Jika Anda tidak peduli tentang kinerja daripada saya akan menyarankan cara yang lebih pythonic akan menggunakan sys.stderr. Menulis dan bukan WTF?!? ">>" karakter. Jika namespace sys.stdout ini terlalu panjang, Anda dapat mengganti nama ... (mis. Dari sys import stderr sebagai stderr_fh). Kemudian Anda dapat melakukan stderr_fh.write ("blah")
ThePracticalOne
1
[3/3] Bahkan jika tolok ukur ini lebih akurat, mungkin tidak perlu dikhawatirkan. Seperti Knuth menulis: "Programer menghabiskan banyak waktu memikirkan, atau mengkhawatirkan, kecepatan bagian nonkritis dari program mereka, dan upaya efisiensi ini sebenarnya memiliki dampak negatif yang kuat ketika debugging dan pemeliharaan dipertimbangkan. Kita harus melupakan sedikit efisiensi, katakanlah sekitar 97% dari waktu: optimasi prematur adalah akar dari semua kejahatan. "
Corey Goldberg
1

Jika Anda ingin keluar dari program karena kesalahan fatal, gunakan:

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

dan import sysdi header.

feli_x
sumber
-2

Jawaban atas pertanyaannya adalah: Ada cara berbeda untuk mencetak stderr dengan python tetapi itu tergantung pada 1.) versi python yang kita gunakan 2.) apa output yang kita inginkan.

Perbedaan antara fungsi tulis cetak dan stderr: stderr : stderr (standard error) adalah pipa yang dibangun ke dalam setiap sistem UNIX / Linux, ketika program Anda crash dan mencetak informasi debugging (seperti traceback dengan Python), ia pergi ke stderr pipa.

print : print adalah pembungkus yang memformat input (input adalah ruang antara argumen dan baris baru di akhir) dan kemudian memanggil fungsi tulis dari objek yang diberikan, objek yang diberikan secara default adalah sys.stdout, tapi kita bisa lewat file yaitu kita dapat mencetak input dalam file juga.

Python2: Jika kita menggunakan python2 maka

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

Python2 trailing comma telah di Python3 menjadi parameter, jadi jika kita menggunakan trailing koma untuk menghindari baris baru setelah cetak, ini akan di Python3 terlihat seperti cetak ('Text to print', end = '') yang merupakan kesalahan sintaksis di bawah Python2 .

http://python3porting.com/noconv.html

Jika kita periksa sceario di atas yang sama di python3:

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

Di bawah Python 2.6 ada impor di masa depan untuk membuat cetak menjadi fungsi. Jadi untuk menghindari kesalahan sintaks dan perbedaan lain, kita harus memulai file mana pun yang menggunakan print () dengan dari impor print_function di masa depan . The masa depan impor hanya bekerja di bawah Python 2.6 dan kemudian, jadi untuk Python 2.5 dan sebelumnya Anda memiliki dua pilihan. Anda dapat mengonversi cetakan yang lebih kompleks menjadi sesuatu yang lebih sederhana, atau Anda dapat menggunakan fungsi cetak terpisah yang berfungsi di bawah Python2 dan Python3.

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

Kasus: Poin yang perlu dicatat bahwa sys.stderr.write () atau sys.stdout.write () (stdout (output standar) adalah pipa yang dibangun di setiap sistem UNIX / Linux) bukan pengganti untuk cetak, tapi ya kita dapat menggunakannya sebagai alternatif dalam beberapa kasus. Print adalah pembungkus yang membungkus input dengan spasi dan baris baru di bagian akhir dan menggunakan fungsi tulis untuk menulis. Ini adalah alasan sys.stderr.write () lebih cepat.

Catatan: kami juga dapat melacak dan menyangkal menggunakan Logging

#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

Vinay Kumar
sumber