在 Spyder for Python 中将控制台打印到日志文件是不可逆的

Posted

技术标签:

【中文标题】在 Spyder for Python 中将控制台打印到日志文件是不可逆的【英文标题】:Printing the console to a log file is not reversible in Spyder for Python 【发布时间】:2021-09-16 20:47:36 【问题描述】:

我正在使用 Spyder for Python,有时我想将控制台打印到日志文件中(在输出很长的情况下),有时我只想在控制台上输出。为此,我在我的 Python 文件中使用了以下结构:

在文件的开头:

import sys
# specify if the output should be printed on a separate log file or on the console
printLogToFile = False

if printLogToFile == True:
    #Specify output file for the logs
   sys.stdout = open('C:/Users/User 1/logfile.txt', 'w')

文件末尾:

# Close the log file if output is printed on a log file and not on the console
if printLogToFile == True:
    sys.stdout.close()
    sys.stdout = sys.__stdout__

基本上,每当我的布尔变量printLogToFile 的值为 False 时,所有内容都会按原样打印在控制台上,并且只要它的值为 True,所有内容都会打印到日志文件中。但是,一旦我只运行一次带有printLogToFile=True 的文件,就不能再反转了。即使变量的值为 False,它仍然会将所有内容打印到日志文件中,而不是控制台上。更奇怪的是,对于与该文件没有任何连接的其他 Python 文件,控制台也不再打印到控制台上。解决此问题的唯一方法是关闭 Spyder 并重新启动它。

您知道为什么会发生这种情况以及如何避免这种情况吗?我会很感激每一条评论。

【问题讨论】:

首先想到的是 Spyder 中的控制台是 IPython 控制台,而不是普通的 Python 控制台。重定向会影响您在 Spyder 中打开的其他 Python 文件,这并不让我感到惊讶,因为它们都共享同一个控制台。作为初始解决方法,您应该能够在 Spyder 中重新启动内核,而不必重新启动整个 IDE。 接下来,docs for sys.__stdout__它还可以用于将实际文件恢复到已知的工作文件对象,以防它们被损坏的对象覆盖。但是,执行此操作的首选方法是在替换之前显式保存先前的流,然后恢复保存的对象。 感谢 nekomatic 的回答。重新启动内核可以暂时解决问题。但我必须一次又一次地这样做。我不明白你的第二条评论。我仍然认为使用它后命令无法反转有点奇怪,尽管我清楚地关闭了文件末尾的流。 第二条评论意味着您应该先用(例如)prev_stdout = sys.stdout 保存原始标准输出,然后用sys.stdout = prev_stdout 恢复它。我怀疑问题是 IPython 对标准输出做了一些事情,所以在你完成重定向后将它设置为 sys.__stdout__ 并不能正确恢复它。如果我在 Spyder 控制台中输入 sys.stdout,它会返回一个 ipykernel.iostream.OutStream 对象,但 sys.__stdout__ 是不同的。 开个玩笑,抱歉。大多数情况下,当有人标记他们的问题spyder 时,这根本与 Spyder 无关,只是他们碰巧使用 Spyder 作为他们的 IDE。这个问题实际上确实与在 Spyder 中运行脚本和从命令行运行它之间的行为差​​异有关。 【参考方案1】:

Spyder 中的控制台是 IPython 控制台,而不是普通的 Python 控制台,所以我认为 IPython 正在使用 stdout 做一些事情,这会导致您的方法失败。

docs forsys.__stdout__

它还可以用于将实际文件恢复为已知的工作文件 对象,以防它们被损坏的对象覆盖。 但是,执行此操作的首选方法是显式保存 替换之前的上一个流,并恢复保存的对象。

换句话说,试试:

if printLogToFile:
    prev_stdout = sys.stdout
    sys.stdout = open('C:/Users/User 1/logfile.txt', 'w')

# code that generates the output goes here

if printLogToFile:
    sys.stdout.close()
    sys.stdout = prev_stdout

作为替代方案,基于this answer 和this answer 假设Python >= 3.7,您可以使用contextlib 和with 语句来选择性地捕获某些代码的输出。这似乎在 Spyder 4 和 5 中对我有用:

from contextlib import redirect_stdout, nullcontext

if printLogToFile:
    f = open('myfile.txt', 'w')
    cm = redirect_stdout(f)
else:
    cm = nullcontext()

with cm:
    # code that generates the output goes here

如果您想执行整个 Python 脚本 myscript.py 并捕获它输出的所有内容,那么让您的脚本保持不变并从包装脚本中调用它可能更容易:

# put this in the same folder as myscript.py

from contextlib import redirect_stdout

with redirect_stdout(open('myfile.txt', 'w')):
    import myscript
 

如果您想要比这更灵活的东西,可能是时候开始使用logging了。

【讨论】:

感谢 nekomatic 的回答。 with cm: # code whose output should be captured goes here print('2+2='.format(4)) 是什么意思?我的代码很长(超过 2000 行)。所有这些代码都应该放在with cm 部分吗?这不会那么好,因为你有一个额外的缩进那里 感谢您的回答 nekomatic。我想我将重新启动内核并使用我的旧解决方案,因为我希望将所有内容都放在一个文件中(没有很多额外的缩进)。也许我会找到一种真正关闭 sys.stout 的方法,因为显然这并没有真正起作用(我也尝试过使用 # Close the log file if output is printed on a log file and not on the console if printLogToFile == True: sys.stdout.close() prev_stdout = sys.stdout sys.stdout = prev_stdout sys.stdout = sys.__stdout__ 的方法,但它没有改变任何东西) 根本不要使用sys.__stdout__,只需在要捕获其输出的代码之后使用sys.stdout = prev_stdout 感谢 nekomatic 的回答。使用文件末尾的代码# Close the log file if output is printed on a log file and not on the console if printLogToFile == True: sys.stdout.close() prev_stdout = sys.stdout sys.stdout = prev_stdout 时出现以下错误“ValueError: I/O operation on closed file.” 为避免进一步混淆,我再次编辑了答案。希望其中一种方法对您有用。

以上是关于在 Spyder for Python 中将控制台打印到日志文件是不可逆的的主要内容,如果未能解决你的问题,请参考以下文章

哪个python IDE在运行后提供python控制台(如Spyder)?

如何在 Python 中使用 Spyder 进行高效调试?

Spyder 开始为每个控制台启动单独的 Python 应用程序

spyder中python 3的ipython控制台上的变量问题

如何将ipython添加入spyder的python console中

如何使用spyder