将`sys.stdout`重定向到文件或缓冲区

8

我正在使用Python 3.4进行工作,遇到了一个我不理解的问题:如果我将stdout重定向到文件,我可以捕获子进程的文本。但是,当我将其重定向到Python文件对象时,我停止捕获该输出。我希望能够解释一下这种行为。

我的代码如下:

from multiprocessing import Process

def worker():
    print('forked output')

def output():
    print('redirected')
    p = Process(target=worker)
    p.daemon = True
    p.start()
    p.join()  # wait for subprocess to terminate
    print('end')

在Python 3.4中,redirect_stdout上下文管理器使得获取stdout变得容易(在这种情况下)。
from contextlib import redirect_stdout
from sys import stdout
from tempfile import TemporaryFile


with TemporaryFile(mode='w+', encoding=stdout.encoding) as buf:
    with redirect_stdout(buf):
        output()  # the function defined above
    buf.seek(0)
    s = buf.read()
    print('output from TemporaryFile:')
    print(s)

我可以简单地调用脚本,以获取以下输出:
$ python stackoverflow.py 
output from TemporaryFile:
redirected
forked output
end

这正是我想要的,而且运行良好。

我的困惑在于,如果我将TemporaryFile替换为TextIOWrapper,那么我的脚本的行为会改变。

from io import BytesIO, TextIOWrapper


with TextIOWrapper(BytesIO(), stdout.encoding) as buf:
    with redirect_stdout(buf):
        output()  # the function defined at the start
    buf.seek(0)
    s = buf.read()
    print('output from TextIO:')
    print(s)

现在当我调用程序时,我失去了分叉进程的输出。
$ python stackoverflow.py 
output from TextIO:
redirected
end

发生了什么事情?

我怀疑问题与 TextIOWrapper 对象没有文件描述符有关,而 multiprocessing 使用的 os.fork() 可能正在用另一个替换 TextIOWrapper,但我承认有些困惑(特别是考虑到标准输出似乎是带有实现 fileno()TextIOWrapper)。

>>> from sys import stdout
>>> stdout.fileno()
1
>>> stdout
<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>

谢谢提供的信息。

2
我相信你已经在正确的轨道上了。我没有足够的内容来回答,但是我认为这是一个文件描述符的问题;sys.stdout 有一个系统级别的描述符,而 TextIOWrapper 是 Python 内部的。 - Cyphase
@Cyphase 我几乎完全确信你是正确的。重定向 stdoutstderrstdin 是操作系统级别的操作,而不是 Python 操作。它适用于文件描述符。BytesIO 对象没有文件描述符,因为它严格来说是内存中的字节 blob。TemporaryFile 是可行的方法。在 Linux 上,如果您将文件创建在目录 /dev/shm 中,它永远不会实际上命中磁盘驱动器(尽管,可以说,使用 /tmp 也没有)。 - eestrada
1个回答

1

由于您正在使用多进程,所以应该使用该库提供的标准消息传递基元。不要从子进程调用 print(),这是不良设计。

如果您实际上正在尝试将其与其他人(非Python)的代码一起使用,请改用 subprocess.check_output() 或另一个子进程函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接