Python 如何使已打开的文件可读(例如sys.stdout)?

Python 如何使已打开的文件可读(例如sys.stdout)?,python,python-3.x,stdout,file-read,Python,Python 3.x,Stdout,File Read,我试图以字符串形式获取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

我试图以字符串形式获取
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
的名称/路径


另一个可行的方法是让我在运行主脚本后,将sys.stdout的内容放入字符串中


如果您像我一样有bug,这些可能是相关的:

错误:


我读到的问题没有帮助:


您可以使用以下代码:

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
我了解了更多细节:

并且了解到,
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
似乎不可靠(由于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
类可以包装多个实例,并跨所有实例复制方法调用。因此,对
redirect\u 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()

我以前对这个问题的回答没有我想的那么好()。我认为这个问题的真正答案是简单地使用记录器。直到最近我才知道伐木工人是什么,但他们比我好多了

最好创建一个logger对象,将字符串发送到日志文件和标准输出。它甚至允许您根据阈值级别更精细地路由消息。代码如下:

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
    - https://stackoverflow.com/questions/21494468/about-notset-in-python-logging/21494716#21494716

    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: https://stackoverflow.com/questions/21494468/about-notset-in-python-logging/21494716#21494716

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

我觉得这是一个特别重要的问题/答案,仅供参考,以防您对未设置的
有任何问题。

感谢上帝给了您答案和问题。

您无法获取stdout的内容。您所能做的只是写入,而不是保存以供回读。如果您想,可以将标准输出设置为您创建的具有读写权限的特定文件。@trigangle应该可以工作!只要标准python字符串函数仍然有效!只有一件事:您可以使用
print(“blob”,stdout=)
来打印到特定的文件。它将无法执行此操作。您需要更改打印功能以同时打印到
sys.\uuu stdout.\uuuuu
和您的特定文件。此行的作用是:
\uuuu builtins.\uuuu.print(*args,file=sys.\uu stdout.\uuu)
?为什么不做一些类似于
print=\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。原因是一旦我重新定义了print函数,旧函数就消失了。因此,调用
\uuuuuu内置\uuuuuu.print
允许我回调原始的print函数。
file=
处理程序告诉打印函数打印到正确的文件<代码>系统。\uuu标准输出\uuu
是终端的原始标准输出。因此,当您打印时,您正在打印到终端并将所有内容保存在一个文件中。这也是我的方法-我在这里唯一可以做的不同的事情是
在脚本运行时打开
文件,并在脚本完成时关闭它。我想这取决于你调用
print
的次数。@trigangle+1太棒了!我一直在尝试获取指向原始打印的指针,但出现了循环,或者有
\uuuu future\uu
向我投诉。我不知道
\uuuuu内置物\uuuuu
@trigangle太棒了!您记住了
a+
,因此如果文件不存在,就会创建该文件。我想我想要的是能够重定向并正常打印到终端。“这样行吗?”@Pinocchio原始代码没有按照你的要求运行。但是,如果您阅读了原始答案下方添加的附录,则会提供新代码,允许正常重定向和打印到终端。
__main__:DEBUG:-> debug
__main__:INFO:-> info
__main__:WARNING:-> warning
__main__:ERROR:-> error
__main__:CRITICAL:-> critical