如何使一个已经打开的文件可读(例如 sys.stdout)?
How does one make an already opened file readable (e.g. sys.stdout)?
我试图在字符串中获取 sys.stdout
的内容。我尝试了显而易见的:
def get_stdout():
import sys
print('a')
print('b')
print('c')
repr(sys.stdout)
contents = ""
#with open('some_file.txt','r') as f:
#with open(sys.stdout) as f:
for line in sys.stdout.readlines():
contents += line
print(contents)
但这给出了错误:
Exception has occurred: UnsupportedOperation
not readable
那么我该如何更改已打开文件的权限?
我试过了:
sys.stdout.mode = 'r'
但这仍然给出相同的错误...
其他可行的方法是以独立于硬件的方式让我获得 stdout
的 name/path。
另一件有用的事情是让我在 运行 我的主脚本在一个字符串中之后将 sys.stdout
的内容。
如果您遇到像我这样的错误,这些可能是相关的:why __builtins__ is both module and dict Python: What's the difference between __builtin__ and __builtins__?
错误:
line 37, in my_print
__builtins__["print"](*args, file=f) # saves to file
TypeError: 'module' object is not subscriptable
我读过的问题没有帮助:
- Making File Writable and Readable in Python
- TypeError: expected str, bytes or os.PathLike object, not _io.TextIOWrapper
- Send the contents from unmodified print statement by e-mail in python
您可以使用以下代码:
import sys
from builtins import print as builtin_print
myfile = "output.txt"
def print(*args):
builtin_print(*args, file=sys.__stdout__) # prints to terminal
with open(myfile, "a+") as f:
builtin_print(*args, file=f) # saves in a file
这应该重新定义 print
函数,以便它打印到 stdout
和您的文件。然后您可以从文件中读取。
我想分享我正在使用的代码,灵感来自已接受的答案:
def my_print(*args, filepath="~/my_stdout.txt"):
"""Modified print statement that prints to terminal/scree AND to a given file (or default).
Note: import it as follows:
from utils.utils import my_print as print
to overwrite builtin print function
Keyword Arguments:
filepath {str} -- where to save contents of printing (default: {'~/my_stdout.txt'})
"""
import sys
from builtins import print as builtin_print
filepath = Path(filepath).expanduser()
# do normal print
builtin_print(*args, file=sys.__stdout__) # prints to terminal
# open my stdout file in update mode
with open(filepath, "a+") as f:
# save the content we are trying to print
builtin_print(*args, file=f) # saves to file
请注意 a+
如果文件不存在则能够创建该文件。
请注意,如果您想删除自定义的旧内容my_stdout.txt
,您需要删除该文件并检查它是否存在:
# remove my stdout if it exists
os.remove(Path('~/my_stdout.txt').expanduser()) if os.path.isfile(Path('~/my_stdout.txt').expanduser()) else None
我想应该就这些了。
编辑:
我遇到了一个错误:
line 37, in my_print
__builtins__["print"](*args, file=f) # saves to file
TypeError: 'module' object is not subscriptable
我查看了更多细节:
- why __builtins__ is both module and dict
- Python: What's the difference between __builtin__ and __builtins__?
并了解到 __builtins__
似乎不可靠(由于 python 实施细节)。
似乎访问内置函数的最可靠方法是使用导入,所以我将其返回给原始回答者给我的代码。
您可以暂时 stdout
重定向到您选择的对象。下面显示的示例将打印数据存储在 StringIO
实例中。一旦上下文管理器块结束,正常打印恢复并允许显示一些调试信息:
#! /usr/bin/env python3
import contextlib
import io
def main():
file = io.StringIO()
with contextlib.redirect_stdout(file):
print('a')
print('b')
print('c')
print(f'{file!r}\n{file.getvalue()!r}\n{file.getvalue()!s}')
if __name__ == '__main__':
main()
附录:
如果您希望像平常一样使用 stdout
并且仍然捕获打印到它的内容,您可能需要使用以下示例。 Apply
class 可以包装多个实例并在所有实例中重复调用方法。因此,对 redirect_stdout
的调用稍作修改:
#! /usr/bin/env python3
import contextlib
import io
import sys
def main():
file = io.StringIO()
with contextlib.redirect_stdout(Apply(sys.stdout, file)):
print('a')
print('b')
print('c')
print(f'{file!r}\n{file.getvalue()!r}\n{file.getvalue()!s}')
class Apply:
def __init__(self, *args):
self.__objects = args
def __getattr__(self, name):
attr = _Attribute(getattr(obj, name) for obj in self.__objects)
setattr(self, name, attr)
return attr
class _Attribute:
def __init__(self, iterable):
self.__attributes = tuple(filter(callable, iterable))
def __call__(self, *args, **kwargs):
return [attr(*args, **kwargs) for attr in self.__attributes]
if __name__ == '__main__':
main()
我之前对这个问题的回答并没有我想象的那么好()。我认为这个问题的真正答案是简单地使用记录器。直到最近我才知道记录器是什么,但它们要好得多。
最好创建一个记录器对象,将您的字符串发送到日志文件和标准输出。它甚至允许您根据阈值级别更精细地路由消息。这是代码:
def logger_SO_print_and_write_to_my_stdout():
"""My sample logger code to print to screen and write to file (the same thing).
Note: trying to replace this old answer of mine using a logger:
- https://github.com/CoreyMSchafer/code_snippets/tree/master/Logging-Advanced
Credit:
- https://www.youtube.com/watch?v=jxmzY9soFXg&t=468s
- https://github.com/CoreyMSchafer/code_snippets/tree/master/Logging-Advanced
-
Other resources:
- https://docs.python-guide.org/writing/logging/
- https://docs.python.org/3/howto/logging.html#logging-basic-tutorial
"""
from pathlib import Path
import logging
import os
import sys
from datetime import datetime
## create directory (& its parents) if it does not exist otherwise do nothing :)
# get current time
current_time = datetime.now().strftime('%b%d_%H-%M-%S')
logs_dirpath = Path(f'~/logs/python_playground_logs_{current_time}/').expanduser()
logs_dirpath.mkdir(parents=True, exist_ok=True)
my_stdout_filename = logs_dirpath / Path('my_stdout.log')
# remove my_stdout if it exists (note you can also just create a new log dir/file each time or append to the end of the log file your using)
#os.remove(my_stdout_filename) if os.path.isfile(my_stdout_filename) else None
## create top logger
logger = logging.getLogger(__name__) # loggers are created in hierarchy using dot notation, thus __name__ ensures no name collisions.
logger.setLevel(logging.DEBUG) # note: use logging.DEBUG, CAREFUL with logging.UNSET:
## log to my_stdout.log file
file_handler = logging.FileHandler(filename=my_stdout_filename)
#file_handler.setLevel(logging.INFO) # not setting it means it inherits the logger. It will log everything from DEBUG upwards in severity to this handler.
log_format = "{asctime}:{levelname}:{lineno}:{name}:{message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
formatter = logging.Formatter(fmt=log_format, style='{') # set the logging format at for this handler
file_handler.setFormatter(fmt=formatter)
## log to stdout/screen
stdout_stream_handler = logging.StreamHandler(stream=sys.stdout) # default stderr, though not sure the advatages of logging to one or the other
#stdout_stream_handler.setLevel(logging.INFO) # Note: having different set levels means that we can route using a threshold what gets logged to this handler
log_format = "{name}:{levelname}:-> {message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
formatter = logging.Formatter(fmt=log_format, style='{') # set the logging format at for this handler
stdout_stream_handler.setFormatter(fmt=formatter)
logger.addHandler(hdlr=file_handler) # add this file handler to top logger
logger.addHandler(hdlr=stdout_stream_handler) # add this file handler to top logger
logger.log(logging.NOTSET, 'notset')
logger.debug('debug')
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.critical('critical')
日志内容:
2020-04-16 11:28:24,987:DEBUG:154:__main__:debug
2020-04-16 11:28:24,988:INFO:155:__main__:info
2020-04-16 11:28:24,988:WARNING:156:__main__:warning
2020-04-16 11:28:24,988:ERROR:157:__main__:error
2020-04-16 11:28:24,988:CRITICAL:158:__main__:critical
终端标准输出:
__main__:DEBUG:-> debug
__main__:INFO:-> info
__main__:WARNING:-> warning
__main__:ERROR:-> error
__main__:CRITICAL:-> critical
我觉得这是一个特别重要的 question/answer 参考,以防你遇到问题 UNSET
:About NOTSET in python logging 感谢上帝在那里的回答和问题。
我试图在字符串中获取 sys.stdout
的内容。我尝试了显而易见的:
def get_stdout():
import sys
print('a')
print('b')
print('c')
repr(sys.stdout)
contents = ""
#with open('some_file.txt','r') as f:
#with open(sys.stdout) as f:
for line in sys.stdout.readlines():
contents += line
print(contents)
但这给出了错误:
Exception has occurred: UnsupportedOperation
not readable
那么我该如何更改已打开文件的权限?
我试过了:
sys.stdout.mode = 'r'
但这仍然给出相同的错误...
其他可行的方法是以独立于硬件的方式让我获得 stdout
的 name/path。
另一件有用的事情是让我在 运行 我的主脚本在一个字符串中之后将 sys.stdout
的内容。
如果您遇到像我这样的错误,这些可能是相关的:why __builtins__ is both module and dict Python: What's the difference between __builtin__ and __builtins__?
错误:
line 37, in my_print
__builtins__["print"](*args, file=f) # saves to file
TypeError: 'module' object is not subscriptable
我读过的问题没有帮助:
- Making File Writable and Readable in Python
- TypeError: expected str, bytes or os.PathLike object, not _io.TextIOWrapper
- Send the contents from unmodified print statement by e-mail in python
您可以使用以下代码:
import sys
from builtins import print as builtin_print
myfile = "output.txt"
def print(*args):
builtin_print(*args, file=sys.__stdout__) # prints to terminal
with open(myfile, "a+") as f:
builtin_print(*args, file=f) # saves in a file
这应该重新定义 print
函数,以便它打印到 stdout
和您的文件。然后您可以从文件中读取。
我想分享我正在使用的代码,灵感来自已接受的答案:
def my_print(*args, filepath="~/my_stdout.txt"):
"""Modified print statement that prints to terminal/scree AND to a given file (or default).
Note: import it as follows:
from utils.utils import my_print as print
to overwrite builtin print function
Keyword Arguments:
filepath {str} -- where to save contents of printing (default: {'~/my_stdout.txt'})
"""
import sys
from builtins import print as builtin_print
filepath = Path(filepath).expanduser()
# do normal print
builtin_print(*args, file=sys.__stdout__) # prints to terminal
# open my stdout file in update mode
with open(filepath, "a+") as f:
# save the content we are trying to print
builtin_print(*args, file=f) # saves to file
请注意 a+
如果文件不存在则能够创建该文件。
请注意,如果您想删除自定义的旧内容my_stdout.txt
,您需要删除该文件并检查它是否存在:
# remove my stdout if it exists
os.remove(Path('~/my_stdout.txt').expanduser()) if os.path.isfile(Path('~/my_stdout.txt').expanduser()) else None
我想应该就这些了。
编辑:
我遇到了一个错误:
line 37, in my_print
__builtins__["print"](*args, file=f) # saves to file
TypeError: 'module' object is not subscriptable
我查看了更多细节:
- why __builtins__ is both module and dict
- Python: What's the difference between __builtin__ and __builtins__?
并了解到 __builtins__
似乎不可靠(由于 python 实施细节)。
似乎访问内置函数的最可靠方法是使用导入,所以我将其返回给原始回答者给我的代码。
您可以暂时 stdout
重定向到您选择的对象。下面显示的示例将打印数据存储在 StringIO
实例中。一旦上下文管理器块结束,正常打印恢复并允许显示一些调试信息:
#! /usr/bin/env python3
import contextlib
import io
def main():
file = io.StringIO()
with contextlib.redirect_stdout(file):
print('a')
print('b')
print('c')
print(f'{file!r}\n{file.getvalue()!r}\n{file.getvalue()!s}')
if __name__ == '__main__':
main()
附录:
如果您希望像平常一样使用 stdout
并且仍然捕获打印到它的内容,您可能需要使用以下示例。 Apply
class 可以包装多个实例并在所有实例中重复调用方法。因此,对 redirect_stdout
的调用稍作修改:
#! /usr/bin/env python3
import contextlib
import io
import sys
def main():
file = io.StringIO()
with contextlib.redirect_stdout(Apply(sys.stdout, file)):
print('a')
print('b')
print('c')
print(f'{file!r}\n{file.getvalue()!r}\n{file.getvalue()!s}')
class Apply:
def __init__(self, *args):
self.__objects = args
def __getattr__(self, name):
attr = _Attribute(getattr(obj, name) for obj in self.__objects)
setattr(self, name, attr)
return attr
class _Attribute:
def __init__(self, iterable):
self.__attributes = tuple(filter(callable, iterable))
def __call__(self, *args, **kwargs):
return [attr(*args, **kwargs) for attr in self.__attributes]
if __name__ == '__main__':
main()
我之前对这个问题的回答并没有我想象的那么好(
最好创建一个记录器对象,将您的字符串发送到日志文件和标准输出。它甚至允许您根据阈值级别更精细地路由消息。这是代码:
def logger_SO_print_and_write_to_my_stdout():
"""My sample logger code to print to screen and write to file (the same thing).
Note: trying to replace this old answer of mine using a logger:
- https://github.com/CoreyMSchafer/code_snippets/tree/master/Logging-Advanced
Credit:
- https://www.youtube.com/watch?v=jxmzY9soFXg&t=468s
- https://github.com/CoreyMSchafer/code_snippets/tree/master/Logging-Advanced
-
Other resources:
- https://docs.python-guide.org/writing/logging/
- https://docs.python.org/3/howto/logging.html#logging-basic-tutorial
"""
from pathlib import Path
import logging
import os
import sys
from datetime import datetime
## create directory (& its parents) if it does not exist otherwise do nothing :)
# get current time
current_time = datetime.now().strftime('%b%d_%H-%M-%S')
logs_dirpath = Path(f'~/logs/python_playground_logs_{current_time}/').expanduser()
logs_dirpath.mkdir(parents=True, exist_ok=True)
my_stdout_filename = logs_dirpath / Path('my_stdout.log')
# remove my_stdout if it exists (note you can also just create a new log dir/file each time or append to the end of the log file your using)
#os.remove(my_stdout_filename) if os.path.isfile(my_stdout_filename) else None
## create top logger
logger = logging.getLogger(__name__) # loggers are created in hierarchy using dot notation, thus __name__ ensures no name collisions.
logger.setLevel(logging.DEBUG) # note: use logging.DEBUG, CAREFUL with logging.UNSET:
## log to my_stdout.log file
file_handler = logging.FileHandler(filename=my_stdout_filename)
#file_handler.setLevel(logging.INFO) # not setting it means it inherits the logger. It will log everything from DEBUG upwards in severity to this handler.
log_format = "{asctime}:{levelname}:{lineno}:{name}:{message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
formatter = logging.Formatter(fmt=log_format, style='{') # set the logging format at for this handler
file_handler.setFormatter(fmt=formatter)
## log to stdout/screen
stdout_stream_handler = logging.StreamHandler(stream=sys.stdout) # default stderr, though not sure the advatages of logging to one or the other
#stdout_stream_handler.setLevel(logging.INFO) # Note: having different set levels means that we can route using a threshold what gets logged to this handler
log_format = "{name}:{levelname}:-> {message}" # see for logrecord attributes https://docs.python.org/3/library/logging.html#logrecord-attributes
formatter = logging.Formatter(fmt=log_format, style='{') # set the logging format at for this handler
stdout_stream_handler.setFormatter(fmt=formatter)
logger.addHandler(hdlr=file_handler) # add this file handler to top logger
logger.addHandler(hdlr=stdout_stream_handler) # add this file handler to top logger
logger.log(logging.NOTSET, 'notset')
logger.debug('debug')
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.critical('critical')
日志内容:
2020-04-16 11:28:24,987:DEBUG:154:__main__:debug
2020-04-16 11:28:24,988:INFO:155:__main__:info
2020-04-16 11:28:24,988:WARNING:156:__main__:warning
2020-04-16 11:28:24,988:ERROR:157:__main__:error
2020-04-16 11:28:24,988:CRITICAL:158:__main__:critical
终端标准输出:
__main__:DEBUG:-> debug
__main__:INFO:-> info
__main__:WARNING:-> warning
__main__:ERROR:-> error
__main__:CRITICAL:-> critical
我觉得这是一个特别重要的 question/answer 参考,以防你遇到问题 UNSET
:About NOTSET in python logging 感谢上帝在那里的回答和问题。