I ran across this problem while writing some code for a challenge.
I have some code that I want to write to a particular file if a filename is provided, or standard output if not:
with (open(filename, 'w') if filename else sys.stdout) as file:
do_something(file)
That with
line is a bit long, and doesn't seem readable at a glance. I could put the open
outside the with
:
f = open(filename, 'w') if filename else sys.stdout
with f as file:
do_something(file)
The file should be closed when the with
is exited. I'm done with stdout
in this case, but I could just have well have wanted to use it for other things later on in the program. But the worst thing about this way of doing it seems to be that making a habit of using open
outside a with
expression could lead to forgetting to close files.
I could go the try/finally route:
try:
f = open(filename, 'w') if filename else sys.stdout
do_something(f)
finally:
if f is not sys.stdout:
f.close()
But this seems a bit verbose and reminds me too much of Java, which, as a rule of thumb, probably means it's not Pythonic.
I could write a context manager to hide the "check if it's stdout and close it" logic:
from contextlib import contextmanager
@contextmanager
def file_or_stdout(fname):
if fname:
f = open(fname, 'w')
yield f
f.close()
else:
yield sys.stdout
with file_or_stdout(filename) as file:
do_something(file)
This seems pretty clear, but it also might be overkill. I'm leaning toward this though, as it leaves the do_something
block plenty of room for clarity, and I could extract the file_or_stdout
function into a utility library so it's not cluttering up the file.
Any thoughts?
I think the simplest is the first one :D
Or you can wrap that if in a normal function if you plan to use it more than once:
I don't think you need to use a context manager in this case because there are no other operations between the opening and closing of the file.
Anyway I wouldn't do it like that.
Keep in mind that if you use sys.stdout this way you will close it which means that any attempt to write on it after your
with
statement will result in an error:So why do you need to hide stdout this way? It's probably easier to use StringIO, write to it and then use a print to display it on stdout.