通常我需要将数据输出到文件,或者,如果未指定文件,则输出到stdout.我使用以下代码段:
if target:
with open(target, 'w') as h:
h.write(content)
else:
sys.stdout.write(content)
Run Code Online (Sandbox Code Playgroud)
我想重写它并统一处理两个目标.
在理想的情况下,它将是:
with open(target, 'w') as h:
h.write(content)
Run Code Online (Sandbox Code Playgroud)
但这不会很好,因为离开with块时sys.stdout被关闭,我不想这样做.我不想
stdout = open(target, 'w')
...
Run Code Online (Sandbox Code Playgroud)
因为我需要记住恢复原始标准输出.
有关:
编辑
我知道我可以换行target,定义单独的函数或使用上下文管理器.我寻找一种简单,优雅,惯用的解决方案,不需要超过5行
Wol*_*lph 84
只是在这里开箱即用,自定义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()
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
# 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'
Run Code Online (Sandbox Code Playgroud)
Ble*_*der 26
坚持使用您当前的代码.这很简单,你可以通过浏览它来准确地告诉它正在做什么.
另一种方式是使用内联if:
handle = open(target, 'w') if target else sys.stdout
handle.write(content)
if handle is not sys.stdout:
handle.close()
Run Code Online (Sandbox Code Playgroud)
但这并不比你拥有的短得多,看起来可能更糟.
你也可以使用sys.stdoutunclosable,但这似乎不太Pythonic:
sys.stdout.close = lambda: None
with (open(target, 'w') if target else sys.stdout) as handle:
handle.write(content)
Run Code Online (Sandbox Code Playgroud)
bhd*_*dnx 12
正如Python 中的条件 with 语句中所指出的,Python 3.7 允许使用contextlib.nullcontext:
from contextlib import nullcontext
with open(target, "w") if target else nullcontext(sys.stdout) as f:
f.write(content)
Run Code Online (Sandbox Code Playgroud)
为什么LBYL你可以EAFP?
try:
with open(target, 'w') as h:
h.write(content)
except TypeError:
sys.stdout.write(content)
Run Code Online (Sandbox Code Playgroud)
为什么要重写它以便在必须使其以复杂的方式工作时统一使用with/ asblock?您将添加更多行并降低性能.
沃尔夫的答案的改进
import sys
import contextlib
@contextlib.contextmanager
def smart_open(filename: str, mode: str = 'r', *args, **kwargs):
'''Open files and i/o streams transparently.'''
if filename == '-':
if 'r' in mode:
stream = sys.stdin
else:
stream = sys.stdout
if 'b' in mode:
fh = stream.buffer # type: IO
else:
fh = stream
close = False
else:
fh = open(filename, mode, *args, **kwargs)
close = True
try:
yield fh
finally:
if close:
try:
fh.close()
except AttributeError:
pass
Run Code Online (Sandbox Code Playgroud)
这允许二进制 IO 并将最终无关的参数传递给openiffilename确实是一个文件名。
另一种可能的解决方案:不要试图避免上下文管理器退出方法,只需复制标准输出。
with (os.fdopen(os.dup(sys.stdout.fileno()), 'w')
if target == '-'
else open(target, 'w')) as f:
f.write("Foo")
Run Code Online (Sandbox Code Playgroud)
如果可以在 bodysys.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")
Run Code Online (Sandbox Code Playgroud)
或者更一般地说:
with target if isinstance(target, io.IOBase) else open(target, "w") as f:
f.write("hello world")
Run Code Online (Sandbox Code Playgroud)