Reputation: 55
I'm trying to redirect the STDOUT of a python script to a file.
If STDOUT is imported from sys, the script's output does not get redirected to a file:
from sys import stdout
stdout = open("text", "w")
print("Hello")
However, if I import only sys and use sys.stdout, the script's output is successfully redirected:
import sys
sys.stdout = open("text", "w")
print("Hello")
Why is this? According to this answer the only difference between "import X" and "from X import Y" is the name that is bound. How does this manage to affect stdout?
Upvotes: 3
Views: 777
Reputation: 884
You can save yourself some effort and earn yourself some "Pythonic" points with this little trick:
import sys
print('hello', file=sys.stdout)
Of course, print
already goes to sys.stdout
by default, so maybe I'm missing something. I'm not sure what's going on with the open('text', 'w')
, but it might not be necessary if you do it this way :)
In answer to your question about the variable assignment impact, when you use the =
operator on a variable, you are actually assigning it to the value in the scope dictionary (in this case globals
).
So when you import sys
, sys
gets imported in the globals
dictionary.
So globals
looks like,
{...,
'sys': <module 'sys' (built-in)>}
You can think of the module itself as a dictionary. So when you do sys.stdout=
... that's like doing globals()['sys'].__dict__['stdout'] =...
When you just import stdout
, globals
looks like:
{...,
'stdout': <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>}
Thus when you do stdout=...
you're really directly replacing that key in the dictionary:
globals()['stdout'] = ...
Hopefully that helps add a little clarity!
Upvotes: 0
Reputation:
The way I do it is to create a 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
and then when I want to suppress the stdout on a certain command:
with suppress_stdout():
# suppressed commands
Upvotes: 1
Reputation: 34145
It's the same as:
x = some_object.some_attr
x = open(...)
You're not changing some_object.some_attr
in that case. You're just assigning to a local value.
When you use sys.stdout = ...
you're actually updating the stdout.
Upvotes: 1
Reputation: 365657
Yes, the only difference is that the name Y
is bound to X.Y
.
Either way, binding Y
to something else isn't going to affect anything in X
.
If it makes it easier, consider this parallel:
>>> y = 2
>>> x = y
>>> x = 3
Do you expect this to change y
to 3
? Of course not. But that's exactly the same thing you're doing.
If it's still not clear, let's break down what those import
s actually do.
When you import sys
, it's equivalent to:
sys.modules['sys'] = __import__('sys')
sys = sys.modules['sys']
sys.stdout = open(text, "w")
But with the from sys import stdout
:
sys.modules['sys'] = __import__('sys')
stdout = sys.modules['sys'].stdout
stdout = open(text, "w")
Upvotes: 4