在 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 forsys.__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)?
Spyder 开始为每个控制台启动单独的 Python 应用程序
spyder中python 3的ipython控制台上的变量问题