为什么我不能使用 "from sys import stdout" 重定向 STDOUT?

Why can I not redirect STDOUT using "from sys import stdout"?

我正在尝试将 python 脚本的 STDOUT 重定向到文件。

如果 STDOUT 是从 sys 导入的,脚本的输出不会被重定向到一个文件:

from sys import stdout
stdout = open("text", "w")
print("Hello")

但是,如果我只导入 sys 并使用 sys.stdout,则脚本的输出成功重定向:

import sys
sys.stdout = open("text", "w")
print("Hello")

这是为什么?根据 this answer,"import X" 和 "from X import Y" 之间的唯一区别是绑定的名称。这如何影响标准输出?

是的,唯一的区别是名称 Y 绑定到 X.Y

无论如何,将 Y 绑定到其他内容不会影响 X.

中的任何内容

如果它更容易,考虑这个平行:

>>> y = 2
>>> x = y
>>> x = 3

您希望这会将 y 更改为 3 吗?当然不是。但这与您正在做的完全一样。


如果还不清楚,让我们分解一下 import 的实际作用。

当你import sys时,相当于:

sys.modules['sys'] = __import__('sys')
sys = sys.modules['sys']
sys.stdout = open(text, "w")

但是 from sys import stdout:

sys.modules['sys'] = __import__('sys')
stdout = sys.modules['sys'].stdout
stdout = open(text, "w")

等于:

x = some_object.some_attr
x = open(...)

在这种情况下,您不会更改 some_object.some_attr。您只是在分配本地值。

当您使用 sys.stdout = ... 时,您实际上是在更新标准输出。

通过这个小技巧,您可以节省一些精力并为自己赢得一些 "Pythonic" 积分:

import sys
print('hello', file=sys.stdout)

当然,默认情况下 print 已经转到 sys.stdout,所以也许我遗漏了什么。我不确定 open('text', 'w') 发生了什么,但如果你这样做可能没有必要:)

在回答有关变量赋值影响的问题时,当您在变量上使用 = 运算符时,实际上是将其赋给范围字典中的值(在本例中 globals).

因此,当您导入 sys 时,sys 会导入到 globals 字典中。

所以 globals 看起来像,

{...,
'sys': <module 'sys' (built-in)>}

你可以把模块本身想象成一本字典。所以当你做 sys.stdout= 时......就像做 globals()['sys'].__dict__['stdout'] =...

当您只导入 stdout 时,globals 看起来像:

{...,
'stdout': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>}

因此,当您执行 stdout=... 时,您实际上是在直接替换字典中的那个键:

globals()['stdout'] = ...

希望这有助于增加一点清晰度!

我的方法是创建一个contextmanager。

@contextmanager
def suppress_stdout():
    with open(os.devnull, 'w') as devnull:
        old_stdout = sys.stdout
        old_stderr = sys.stderr
        sys.stdout = devnull
        try:
            yield
        finally:
            sys.stdout = old_stdout
            sys.stderr = old_stderr

然后当我想抑制某个命令的标准输出时:

with suppress_stdout():
    # suppressed commands