대부분의 경우 파이썬을 배우면서 with를 처음 접하는 때는 파일을 다룰 때이다. 어떤 파일을 다루고 나서는 close를 해주어야 프로그램이 정상 동작한다. 그러나 파일을 다루던 도중에 오류로 인해 예상치 않게 프로그램이 종료가 된 경우 close를 미처 해주지 못한 경우도 발생한다. try - finally 구문을 이용하면 실행 도중 오류가 발생하였을 때 close를 해 줄 수 있다. 그러나 with를 사용하면 우리가 직접 close를 해 줄 필요가 없다. 컨텍스트 매니저를 통해 자원의 할당과 반납을 필요할 때 해주기 때문이다.
try finally vs with
# Handling file with try - finally
f = open('a.txt','w')
try:
f.write('hello world')
finally:
f.close()
# Handling File with context manager(with as)
with open('a.txt','w') as f:
f.write('hello world')
두 코드 모두 a.txt 파일에 "hello world"를 적고 파일을 닫는다. 두 방법 모두 파일을 작성하던 중 오류가 발생하여도 파일을 닫는데, with as 를 이용한 코드에는 따로 close를 적지 않았다. with as 문을 사용한 코드에선 with as 문 내부의 코드가 모두 실행되거나 중간에 오류가 발생하면 파일이 닫힌다. try finally 구문보다 간결하다.
여러 개의 파일 관리하기
두 개 이상의 파일을 동시에 사용할 때 with as 문을 사용하는 방법이다. 단순히 두 개의 with as 문을 겹쳐도 되고, 하나의 with문에 두 개 이상의 파일을 열어도 된다.
with open('a.txt','w') as a:
with open('b.txt','w') as b:
a.write('hello world')
b.write('hello b')
with open('a.txt','w') as a, open('b.txt','w') as b:
a.write('hello world')
b.write('hello b')
한 번에 다루고자 하는 파일이 많은데 with문을 사용하면 with문이 옆으로 길어진다. 그래서 파이썬 버전 3.10.0 b1 버전부터는 괄호와 함께 여러 개의 파일들을 여러 줄에 걸쳐서 작성할 수 있다.
# from python version 3.10.0b1
with (
open('a.txt','w') as a,
open('b.txt','w') as b,
open('c.txt','w') as c
):
a.write('aaa')
b.write('bbb')
c.write('ccc')
class로 context manager 구현하기
우리가 원하는 class를 with as문에서 작동하도록 할 수 있다. 그러기 위해선 __enter__, __exit__ 두 메서드를 작성해줘야 한다. 우리가 파일 객체를 with구문과 함께 사용할 수 있는 것도 두 메서드가 이미 구현되어있기 때문이다. 예를 들어 Logger 클래스가 있다고 하자. 이 클래스는 파일에 log메서드를 통해 로그를 작성한다. open, close 메서드를 통해 로그가 기록될 파일 객체를 열고 닫을 수 있다. 이 Logger클래스를 with문과 함께 사용하고 싶을 때는 다음과 같이 사용해주면 된다.
class Logger:
def __init__(self,name):
self.name = name
self.file = None
def log(self,content):
now = datetime.now()
timestr = f'[{now.year}-{now.month}-{now.day} {now.hour}:{now.minute}:{now.second}] '
self.file.write(timestr+content+'\n')
def open(self):
self.file = open(self.name,'a')
def close(self):
self.file.close()
def __enter__(self):
self.open()
return self
def __exit__(self, type,value,traceback):
self.log('__exit__ from Logger called.')
self.close()
작성된 Logger에서. enter과 close 메소드만 보자. enter 메서드에서 open, exit 메서드에서 close가 이루어지고 있다. 이처럼 자기가 원하는 클래스를 with문과 함께 사용하고 싶다면 __enter__에 원하는 자원의 할당, 연결 등의 작업을 해 주고 __exit__에서 자원의 제거, 연결 해제 등의 마무리 작업을 해주면 된다.
위 클래스가 정상적으로 작동하는지 확인해보자.
if __name__ == '__main__':
with Logger('log_a.txt') as logger:
logger.log('Started')
logger.log('log a logging')
logger.log('Ended')
with Logger('log_b.txt') as logger:
logger.log('Started')
logger.log('log b logging')
raise Exception()
logger.log('Ended')
첫번째 with 블록에선 모든 로그가 기록될 것이고, 두 번째 블록에선 중간에 예외가 발생하여 Ended가 찍히지 않았을 것이다. 만들어진 파일을 확인해보자.
[2021-5-19 18:7:43] Started
[2021-5-19 18:7:43] log a logging
[2021-5-19 18:7:43] Ended
[2021-5-19 18:7:43] __exit__ from Logger called.
[2021-5-19 18:7:43] Started
[2021-5-19 18:7:43] log b logging
[2021-5-19 18:7:43] __exit__ from Logger called.
여기서, 두번째 로그파일을 보면 중간에 예외가 발생하여도 __exit__이 호출되어 파일 객체의 close작업이 이루어진 것을 볼 수 있다.
'Study > Python' 카테고리의 다른 글
Python collections의 Counter로 개수 세기 (0) | 2021.05.27 |
---|---|
python에서 json 다루기 (0) | 2021.05.24 |
Python에서 AhoCorasick(아호코라식) 알고리즘 구현하기 (0) | 2021.04.17 |
Python 문자열(string) 그냥 한번 대충 보기 (0) | 2021.04.14 |
Python에서 Glob으로 파일 혹은 폴더의 경로 불러오기 (0) | 2021.03.26 |
댓글