`with open (…)`과`sys.stdout`을 모두 멋지게 처리하는 방법은 무엇입니까?
종종 데이터를 파일 또는 파일이 지정되지 않은 경우 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')
...
원래 stdout을 복원하는 것을 기억해야하기 때문입니다.
관련 :
- stdout을 Python의 파일로 리디렉션 하시겠습니까?
- 예외 처리-C ++와 비교하여 Python에서 예외 처리에 대한 흥미로운 기사
편집하다
랩핑 target
하거나 별도의 기능을 정의하거나 컨텍스트 관리자를 사용할 수 있다는 것을 알고 있습니다 . 5 줄 이상 필요하지 않은 간단하고 우아하며 관용적 인 솔루션을 찾습니다.
여기서 상자 밖에서 생각하면 사용자 지정 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'
현재 코드를 고수하십시오. 간단하고 흘끗 쳐다 보는 것만으로 정확히 무엇을하는지 알 수 있습니다 .
또 다른 방법은 인라인을 사용하는 것입니다 if
.
handle = open(target, 'w') if target else sys.stdout
handle.write(content)
if handle is not sys.stdout:
handle.close()
그러나 그것은 당신이 가진 것보다 훨씬 짧지 않으며 틀림없이 더 나빠 보입니다.
sys.stdout
닫을 수 없게 만들 수도 있지만 너무 파이썬 적으로 보이지는 않습니다.
sys.stdout.close = lambda: None
with (open(target, 'w') if target else sys.stdout) as handle:
handle.write(content)
EAFP를 할 수 있는데 왜 LBYL인가?
try:
with open(target, 'w') as h:
h.write(content)
except TypeError:
sys.stdout.write(content)
복잡한 방식으로 작동해야 할 때 with
/ as
블록을 균일하게 사용하도록 다시 작성하는 이유는 무엇 입니까? 더 많은 선을 추가 하고 성능을 떨어 뜨립니다.
Wolph의 답변 개선
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
This allows binary IO and pass eventual extraneous arguments to open
if filename
is indeed a file name.
Another possible solution: do not try to avoid the context manager exit method, just duplicate stdout.
with (os.fdopen(os.dup(sys.stdout.fileno()), 'w')
if target == '-'
else open(target, 'w')) as f:
f.write("Foo")
I'd also go for a simple wrapper function, which can be pretty simple if you can ignore the mode (and consequently stdin vs. stdout), for example:
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
Okay, if we are getting into one-liner wars, here's:
(target and open(target, 'w') or sys.stdout).write(content)
I like Jacob's original example as long as context is only written in one place. It would be a problem if you end up re-opening the file for many writes. I think I would just make the decision once at the top of the script and let the system close the file on exit:
output = target and open(target, 'w') or sys.stdout
...
output.write('thing one\n')
...
output.write('thing two\n')
You could include your own exit handler if you think its more tidy
import atexit
def cleanup_output():
global output
if output is not sys.stdout:
output.close()
atexit(cleanup_output)
How about opening a new fd for sys.stdout? This way you won't have any problems closing it:
if not target:
target = "/dev/stdout"
with open(target, 'w') as f:
f.write(content)
If you really must insist on something more "elegant", i.e. a one-liner:
>>> 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
appears and contains the text foo
.
if (out != sys.stdout):
with open(out, 'wb') as f:
f.write(data)
else:
out.write(data)
Slight improvement in some cases.
참고URL : https://stackoverflow.com/questions/17602878/how-to-handle-both-with-open-and-sys-stdout-nicely
'programing tip' 카테고리의 다른 글
오류 / langversion에 대한 잘못된 옵션 '6'; (0) | 2020.10.08 |
---|---|
커서 위치를 잃지 않고 입력 값 업데이트 (0) | 2020.10.08 |
Mac을 소유하지 않고 iOS 앱을 만드시나요? (0) | 2020.10.08 |
테스트 케이스에 사용되는 "setUp"및 "tearDown"Python 메서드를 설명합니다. (0) | 2020.10.08 |
인덱스로 문자열에서 문자를 얻는 방법? (0) | 2020.10.08 |