110

多くの場合、データをファイルに出力するか、ファイルが指定されていない場合は標準出力に出力する必要があります。次のスニペットを使用します。

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)

しかし、ブロックを離れるときにsys.stdoutが閉じられwith、私はそれを望まないため、これはうまく機能しません。私もしたくない

stdout = open(target, 'w')
...

元の標準出力を復元することを覚えておく必要があるためです。

関連している:

編集

targetラップしたり、別の関数を定義したり、context managerを使用したりできることを知っています。5行以上を必要としない、シンプルでエレガントで慣用的なソリューションを探しています

4

13 に答える 13

108

ここで枠にとらわれずに考えてみると、カスタム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()

次のように使用します。

# For Python 2 you need this line
from __future__ import print_function

# writes to some_file
with smart_open('some_file') as fh:
    print('some output', file=fh)

# writes to stdout
with smart_open() as fh:
    print('some output', file=fh)

# writes to stdout
with smart_open('-') as fh:
    print('some output', file=fh)
于 2013-07-11T20:35:51.137 に答える
35

現在のコードに固執します。シンプルで、一目見ただけで何をしているのか正確にわかります。

別の方法は inline を使用することifです:

handle = open(target, 'w') if target else sys.stdout
handle.write(content)

if handle is not sys.stdout:
    handle.close()

しかし、それはあなたが持っているものよりもはるかに短くはなく、間違いなく悪く見えます.

unclosable にすることもできますがsys.stdout、それはあまりにも Pythonic ではないようです:

sys.stdout.close = lambda: None

with (open(target, 'w') if target else sys.stdout) as handle:
    handle.write(content)
于 2013-07-11T20:37:29.697 に答える
5

別の可能な解決策: コンテキスト マネージャーの exit メソッドを回避しようとせず、stdout を複製するだけです。

with (os.fdopen(os.dup(sys.stdout.fileno()), 'w')
      if target == '-'
      else open(target, 'w')) as f:
      f.write("Foo")
于 2014-09-30T13:49:51.713 に答える
1

さて、ワンライナー戦争に入る場合は、次のとおりです。

(target and open(target, 'w') or sys.stdout).write(content)

コンテキストが 1 か所にしか書かれていない限り、Jacob の元の例が好きです。多くの書き込みのためにファイルを再度開いてしまうと、問題が発生します。スクリプトの先頭で一度だけ決定を下し、システムが終了時にファイルを閉じるようにすると思います。

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)
于 2013-07-11T21:58:09.103 に答える
1

また、単純なラッパー関数も使用します。これは、モードを無視できる場合 (したがって、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
于 2013-07-11T21:26:12.840 に答える
1
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 以降を使用している場合は、2 行追加するだけです。1 行は追加用でimport、1 行はstack.enter_context.

于 2020-05-11T21:11:47.040 に答える
0

sys.stdout 用の新しい fd を開くのはどうですか? このようにして、問題なく閉じることができます:

if not target:
    target = "/dev/stdout"
with open(target, 'w') as f:
    f.write(content)
于 2014-12-09T09:31:01.147 に答える
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

于 2013-07-11T21:45:27.690 に答える