如何在 Python 中打印到 stderr?

Posted

技术标签:

【中文标题】如何在 Python 中打印到 stderr?【英文标题】:How to print to stderr in Python? 【发布时间】:2011-07-31 06:56:28 【问题描述】:

有几种写入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 的 zen 相矛盾 ,那么这里有什么区别,一种或另一种方式有什么优点或缺点吗?应该使用哪种方式?

应该有一种——最好只有一种——明显的方法。

【问题讨论】:

列出的第一种方式是 Python 3 中删除的许多内容之一。共识似乎是 >> 语法无论如何都很难看,并且由于 print 现在是一个函数,因此该语法永远不会起作用. 我使用:sys.exit('Error: ') 只使用打印。 【参考方案1】:

我这样做只是为了好玩,但这是另一种方式... :-)

message = 'error: Belly up!!'
print(message, file=sys.stderr if 'error' in message.lower() else sys.stdout)

【讨论】:

【参考方案2】:

我发现这是唯一一个简短、灵活、便携和易读的:

# This line only if you still care about Python2
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

【讨论】:

想一想:由于这将导入打印功能,因此原始脚本中的每个其他“打印”现在都需要“功能化”添加“(”和“)”。所以这是对这种方法的轻微打击,IMO。 @DanH 是的,这强制你让你的代码准备好 Python3。我想这可能是很多人真正喜欢它的原因! @MarkH... 是的,它迫使您使您的代码准备好 Python3... 它还迫使您现在就这样做,只是将一些调试信息打印到标准错误...在大多数情况下,当我尝试调试某些东西时,我会发现更多的麻烦。 (我宁愿不引入新的语法错误!):-) FWIW 此代码不会强制您在整个程序中使用 print 的函数版本。仅在包含eprint() 定义的模块中。将其单独放在一个小文件中,从中导入eprint 到您的其他文件中,您可以继续使用语句print,只要您愿意。 此外,您从打印功能转换为打印功能是一个简单的查找替换,2to3 已经为您自动化。如果你还没有,就已经这样做了; python2 将在不到 2 年的时间内消失...... 2 到 3 之间的一些事情可能会变得有点棘手;打印功能不是其中之一。见docs.python.org/2/library/2to3.html【参考方案3】:

Python 2:

print >> sys.stderr, "fatal error"

Python 3:

print("fatal error", file=sys.stderr)

长答案

print >> sys.stderr 在 Python3 中消失了。 http://docs.python.org/3.0/whatsnew/3.0.html 说:

旧:print >> sys.stderr, "fatal error" 新:print("fatal error", file=sys.stderr)

对于我们中的许多人来说,将目的地放在命令末尾有点不自然。另一种选择

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

看起来更面向对象,并且优雅地从通用到特定。但请注意,write 不是 print 的 1:1 替代品。

【讨论】:

我想这是一个偏好问题,但我看不出print('spam', file=sys.stderr) 有什么丑陋之处。如果您一遍又一遍地这样做,您可以像在最流行的答案中那样编写“eprint”功能,但在这种情况下,我会问,日志记录有什么问题? ***.com/a/41304513/1450294 另一种澄清意图的方法是在缩进调用print("ERROR", file=dest)之前执行with sys.stderr as dest:【参考方案4】:

我会说你的第一种方法:

print >> sys.stderr, 'spam' 

是“一种......

-- 2020 年编辑--

以上是我在 2011 年对 Python 2.7 的回答。既然 Python 3 是标准,我认为“正确”的答案是:

print("spam", file=sys.stderr) 

【讨论】:

意见不同。这对我来说是最不明显的。 @AliVeli 没有括号,这是较旧的 Python 我会说这是所有3中最丑的版本 >> 在语法上是什么意思?我知道复制 bash 的 > 是一种努力,所以这样做是不是有些硬道理? 打印 >> sys.stderr, 'test' 不再工作 (python3)【参考方案5】:

还没有人提到logging,但是专门创建了日志记录以传达错误消息。基本配置将设置一个流处理程序写入标准错误。

这个脚本:

# 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 将包含打印在标准输出上的“hello world”。

【讨论】:

根据我的经验,使用打印记录消息的人比使用日志记录的人多。我认为 python4 应该从语言中删除 print 并强制您为此使用日志记录。 这是最好的答案!! ...我在 print 或 sys 上苦苦挣扎,或者谁知道...何时需要适当的日志记录...感谢您的好主意 @Mnebuerquo Java 做到了……现在我们有了 Kotlin。【参考方案6】:

如果您因为致命错误而想退出程序,请使用:

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

import sys 在标题中。

【讨论】:

【参考方案7】:

在 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之外的任何内容。

【讨论】:

【参考方案8】:
import sys
sys.stderr.write()

是我的选择,只是更具可读性,准确地说明您打算做什么,并且可以跨版本移植。

编辑:对我来说,成为“pythonic”是关于可读性和性能的第三个想法……考虑到这两点,使用 python,你的代码 80% 将是 pythonic。列表理解是不经常使用的“大事”(可读性)。

【讨论】:

别忘了冲洗。 print 语句的优点是可以轻松打印非字符串值,而无需先转换它们。如果您需要打印语句,我建议您使用第三个选项来准备好 python 3 sys.stderr.write()print 完全不同。它不添加换行符。 这适用于 Python 2 和 3,如果您想同时支持两者,这一点很重要。 @SkipHuffman 你的意思是os.linesep。毕竟这是stderr。不希望控制台弄乱错误的换行符。【参考方案9】:

问题的答案是:在 python 中打印 stderr 有不同的方法,但这取决于 1.) 我们使用的是哪个 python 版本 2.) 我们想要什么确切的输出。

print和stderr的write函数的区别: stderr :stderr(标准错误)是每个 UNIX/Linux 系统中内置的管道,当您的程序崩溃并打印出调试信息(如 Python 中的回溯)时,它会进入 stderr 管道。

print:print 是一个包装器,用于格式化输入(输入是参数和末尾的换行符之间的空格),然后它调用给定对象的 write 函数,给定对象默认情况下是 sys.stdout,但我们可以传递一个文件,即我们也可以在文件中打印输入。

Python2: 如果我们使用的是python2,那么

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

Python2 的尾随逗号在 Python3 中已成为参数,所以如果我们使用 尾随逗号以避免打印后的换行符,这将在 Python3 看起来像 print('Text to print', end=' ') 这是一种语法 Python2下报错。

http://python3porting.com/noconv.html

如果我们在 python3 中检查上述相同的情况:

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

在 Python 2.6 下,有一个 future 导入可以将 print 转换为 功能。因此,为了避免任何语法错误和其他差异,我们 应该从 future 导入任何我们使用 print() 的文件开始 打印函数。 future 导入仅适用于 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() (标准输出(标准输出)是一个内置于每个 UNIX/Linux 系统)不是打印的替代品,但是我们可以使用 在某些情况下,它可以作为替代方案。 Print 是一个包装器,用于包装 最后输入空格和换行符,并使用 write 函数 写。这就是 sys.stderr.write() 更快的原因。

注意:我们还可以使用 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

【讨论】:

【参考方案10】:

对于 Python 2,我的选择是: print >> sys.stderr, 'spam' 因为您可以简单地打印列表/字典等,而无需将其转换为字符串。 print >> sys.stderr, 'spam': 'spam' 代替: sys.stderr.write(str('spam': 'spam'))

【讨论】:

更 Pythonic 的打印字典的方法是使用 "0".format('spam': 'spam') 之类的东西,不是吗?我会说你应该避免显式转换为字符串。编辑:我不小心语法了 @luketparkinson 这都是关于调试的——所以,我认为,最好尽可能使用最简单的代码。 这不适用于 Python 3,因此您应该在新代码中避免使用它。【参考方案11】:

编辑事后看来,我认为更改 sys.stderr 和没有看到行为更新的潜在混淆使得这个答案不如其他人指出的那样仅仅使用一个简单的函数。

使用 partial 只会为您节省 1 行代码。潜在的混乱不值得节省 1 行代码。

原创

为了更简单,这里有一个使用“部分”的版本,这对包装函数有很大帮助。

from __future__ import print_function
import sys
from functools import partial

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

然后你就这样使用它

error('An error occured!')

您可以通过执行以下操作(覆盖来自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,请使用本页aaguirre 提到的**kwargs 方法。

【讨论】:

Corey Goldberg 的代码最好在 Rube Goldberg 机器上运行吗? :P 顺便说一句:如果您想了解更多关于“部分”的信息,“currying”是一个(更)有用的搜索关键字。【参考方案12】:

我正在使用 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]$

好吧,除了 python 给你的小介绍已经被stderr(它还能去哪里?)这一事实之外,它可以工作。

【讨论】:

【参考方案13】:

这同样适用于标准输出:

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

正如其他答案中所述,print 提供了一个漂亮的界面,通常更方便(例如打印调试信息),而 write 更快,也可以当您必须以某种方式精确格式化输出时更方便。我也会考虑可维护性:

    您以后可能会决定在 stdout/stderr 和常规文件之间切换。

    print() 语法在 Python 3 中发生了变化,所以如果你需要同时支持这两个版本,write() 可能会更好。

【讨论】:

使用from __future__ import print_function 是同时支持 Python 2.6+ 和 Python 3 的更好方法。【参考方案14】:

我使用 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")

【讨论】:

我打算建议您也可以使用部分,但意识到部分在创建部分时将标准错误分配给函数。这可以防止您稍后重定向 stderr,因为部分仍将保留原始 stderr 对象。【参考方案15】:

这将模仿标准打印功能,但输出到标准错误

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

【讨论】:

我会添加一个 sys.stderr.flush() @AMS - 为什么? print 不包括冲洗。 当你能做到的时候为什么要模仿?【参考方案16】:

如果你做一个简单的测试:

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)的方式。我以前从未使用过它(惯性),但我想我会运行这个计时脚本并添加打印功能。无法直接比较 print 语句和函数(从 future 导入适用于整个文件并屏蔽 print 语句)但是重写此代码以使用 print 函数而不是语句我看到更快的速度向上(〜1.6,虽然有些变量)有利于打印功能。 这个测试的结果在某种程度上具有误导性。打印 'XXXXXXXXXXXXXXXXXXXXX' 而不是 'X' 并且比率下降到 1.05。我假设大多数 python 程序需要打印多个字符。 我不关心性能,比如打印警告。 我知道这已经有一段时间了,但是你在我的帖子之后同样长时间地回复了......如果你不关心性能而不是我建议更pythonic的方法是使用 sys.stderr。写而不是WTF?!? “>>”字符。如果这个 sys.stdout 命名空间太长,你可以重命名它...(即 from sys import stderr as stderr_fh)。然后你可以做 stderr_fh.write("blah") [3/3] 即使这个基准更准确,也可能不值得担心。正如 Knuth 所写:“程序员浪费了大量时间来思考或担心他们程序中非关键部分的速度,而在考虑调试和维护时,这些效率上的尝试实际上会产生强烈的负面影响。我们应该忘记小的效率,大约 97% 的时间:过早优化是万恶之源。”

以上是关于如何在 Python 中打印到 stderr?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Python中异步记录stdout / stderr?

你如何在 R 中打印到标准错误?

如何使 xcodebuild 向 stderr 打印编译错误和警告?

有没有办法在谷歌部署管理器中打印到stdout,stderr或日志文件?

使用python sh模块,如何保存组合的stdout和stderr?

如何在实时输出的同时将命令的 stdout 和 stderr 重定向到控制台和日志文件?