通常我需要将数据输出到文件,或者,如果未指定文件,则输出到stdout。我使用以下代码段:
if target:
with open(target, 'w') as h:
h.write(content)
else:
sys.stdout.write(content)
我想重写它并统一处理两个目标。
在理想的情况下,它将是:
with open(target, 'w') as h:
h.write(content)
但这不会很好,因为离开with
块时sys.stdout将被关闭,我不想这样做。我不想
stdout = open(target, 'w')
...
因为我需要记住恢复原始标准输出。
相关:
修改
我知道我可以包装target
,定义单独的函数或使用context manager。我寻找一个简单,优雅,惯用的解决方案,不需要超过5行
答案 0 :(得分:76)
在这里开箱即用,自定义open()
方法怎么样?
import sys
import contextlib
@contextlib.contextmanager
def smart_open(filename=None):
if filename and filename != '-':
fh = open(filename, 'w')
else:
fh = sys.stdout
try:
yield fh
finally:
if fh is not sys.stdout:
fh.close()
像这样使用:
# writes to some_file
with smart_open('some_file') as fh:
print >>fh, 'some output'
# writes to stdout
with smart_open() as fh:
print >>fh, 'some output'
# writes to stdout
with smart_open('-') as fh:
print >>fh, 'some output'
答案 1 :(得分:26)
坚持使用您当前的代码。这很简单,你可以通过浏览它来告诉完全它正在做什么。
另一种方式是使用内联if
:
handle = open(target, 'w') if target else sys.stdout
handle.write(content)
if handle is not sys.stdout:
handle.close()
但这并不比你所拥有的短得多,而且看起来可能更糟。
你也可以让sys.stdout
取消隐藏,但这似乎不太Pythonic:
sys.stdout.close = lambda: None
with (open(target, 'w') if target else sys.stdout) as handle:
handle.write(content)
答案 2 :(得分:6)
为什么LBYL何时可以使用EAFP?
try:
with open(target, 'w') as h:
h.write(content)
except TypeError:
sys.stdout.write(content)
为什么要重写它以便在必须使其以复杂的方式工作时统一使用with
/ as
块?您将添加更多行并降低性能。
答案 3 :(得分:3)
import sys
import contextlib
@contextlib.contextmanager
def smart_open(filename: str = None, mode: str = 'r', *args, **kwargs):
if filename == '-':
if 'r' in mode:
stream = sys.stdin
else:
stream = sys.stdout
if 'b' in mode:
fh = stream.buffer
else:
fh = stream
else:
fh = open(filename, mode, *args, **kwargs)
try:
yield fh
finally:
try:
fh.close()
except AttributeError:
pass
如果open
确实是文件名,则允许二进制IO并将最终无关的参数传递给filename
。
答案 4 :(得分:2)
我还会选择一个简单的包装函数,如果你可以忽略模式(以及stdin与stdout),这可能非常简单,例如:
from contextlib import contextmanager
import sys
@contextmanager
def open_or_stdout(filename):
if filename != '-':
with open(filename, 'w') as f:
yield f
else:
yield sys.stdout
答案 5 :(得分:2)
另一种可能的解决方案:不要试图避开上下文管理器退出方法,只需复制stdout。
with (os.fdopen(os.dup(sys.stdout.fileno()), 'w')
if target == '-'
else open(target, 'w')) as f:
f.write("Foo")
答案 6 :(得分:1)
好的,如果我们进入单线战争,那么就是:
(target and open(target, 'w') or sys.stdout).write(content)
我喜欢雅各布的原始例子,只要上下文只写在一个地方。如果您最终为许多写入重新打开文件,那将是一个问题。我想我只会在脚本顶部做出一次决定,让系统在退出时关闭文件:
output = target and open(target, 'w') or sys.stdout
...
output.write('thing one\n')
...
output.write('thing two\n')
如果您认为它更整洁
,您可以包含自己的退出处理程序import atexit
def cleanup_output():
global output
if output is not sys.stdout:
output.close()
atexit(cleanup_output)
答案 7 :(得分:1)
如何为sys.stdout打开一个新的fd?这样你关闭它就不会有任何问题:
if not target:
target = "/dev/stdout"
with open(target, 'w') as f:
f.write(content)
答案 8 :(得分:0)
如果你真的必须坚持更“优雅”的东西,即单行:
>>> import sys
>>> target = "foo.txt"
>>> content = "foo"
>>> (lambda target, content: (lambda target, content: filter(lambda h: not h.write(content), (target,))[0].close())(open(target, 'w'), content) if target else sys.stdout.write(content))(target, content)
foo.txt
出现并包含文字foo
。
答案 9 :(得分:0)
if (out != sys.stdout):
with open(out, 'wb') as f:
f.write(data)
else:
out.write(data)
在某些情况下略有改善。
答案 10 :(得分:0)
import contextlib
import sys
with contextlib.ExitStack() as stack:
h = stack.enter_context(open(target, 'w')) if target else sys.stdout
h.write(content)
如果您使用的是Python 3.3或更高版本,则只需增加两行:一行用于额外的import
,另一行用于stack.enter_context
。
答案 11 :(得分:0)
如果sys.stdout
正文后关闭with
是好的,您还可以使用以下模式:
# Use stdout when target is "-"
with open(target, "w") if target != "-" else sys.stdout as f:
f.write("hello world")
# Use stdout when target is falsy (None, empty string, ...)
with open(target, "w") if target else sys.stdout as f:
f.write("hello world")
或更普遍地:
with target if isinstance(target, io.IOBase) else open(target, "w") as f:
f.write("hello world")
答案 12 :(得分:0)
以下解决方案不是美,而是很久以前的事情;就在...
handler = open(path, mode = 'a') if path else sys.stdout
try:
print('stuff', file = handler)
... # other stuff or more writes/prints, etc.
except Exception as e:
if not (path is None): handler.close()
raise e
handler.close()