앎을 경계하기

[가짜연구소3기] Data Engineer

[가짜연구소 3기] 데이터 엔지니어링 45 - Context managers

양갱맨 2021. 9. 13. 13:38

주제

컨텍스트 매니저에 대해 배웠다.


Context managers 사용하기

Context managers?

  • 컨텍스트를 설정한다.
  • 코드를 실행시킨다.
  • 컨텍스트를 제거한다.

 

출장 뷔페로 예를 들면,

  • 행사 시작 전, 직원들이 음식 세팅을한다.
  • 행사 진행
  • 행사 끝나면 음식 정리한다.

 

코드에서 예를 들면,

with open('my_file.txt') as my_file:
	text = my_file.read()
	length = len(text)

print('The file is {} characters long'.format(length))

여기서 open() 이 컨텍스트 관리자다.

  • 파일을 열어 사용할 수 있도록 설정한다.
  • 파일을 읽고 길이를 변수에 저장하는 작업을 수행한다.
  • 파일이 닫혔는지 확인한다.

 

with가 컨텍스트 매니저를 사용하겠다는 의미를 갖는다.

with 컨텍스트 매니저(인수..) as 반환된 값 할당할 변수 이름:
	실행할 코드

정리 후 실행될 코드

연습문제

image = get_image_from_instagram()

# Time how long process_with_numpy(image) takes to run
with timer():
  print('Numpy version')
  process_with_numpy(image)

# Time how long process_with_pytorch(image) takes to run
with timer():
  print('Pytorch version')
  process_with_pytorch(image)
Numpy version
Processing..........done!
Elapsed: 1.52 seconds
Pytorch version
Processing..........done!
Elapsed: 0.33 seconds

컨텍스트 매니저 작성하기

컨텍스트 매니저를 만들 수도 있다.

  • 클래스 기반
class File(object):
    def __init__(self, file_name, method):
        self.file_obj = open(file_name, method)
    def __enter__(self):
        return self.file_obj
    def __exit__(self, type, value, trace_back):
        self.file_obj.close()
with File('demo.txt', 'wb') as opened_file:
    opened_file.wirte('Hola!')
  • 함수 기반
@contextlib.contextmanager
def my_context():
	컨텍스트에 필요한 설정 코드 추가 (옵션)
	yield
	컨텍스트 정리에 필요한 코드 추가 (옵션)
  1. 함수 정의
  1. 컨텍스트에 필요한 설정 코드 추가(옵션)
  1. yield 키워드
  1. 컨텍스트 정리에 필요한 코드추가(옵션)
  1. contextlib 패키지 사용해서 데코레이터 표시

yield 의미

쉽게 생각하면 return과 유사하다.

import contextlib

@contextlib.contextmanager
def foo():
	print("hi")
	yield 1
	print("bye")

with foo():
	print("Zzz")
hi
Zzz
bye
import contextlib

@contextlib.contextmanager
def foo():
	print("hi")
	yield 1
	print("bye")

with foo() as f:
	print("Zzz")
	print(f)
hi
Zzz
1
bye

중첩된 컨텍스트, 오류 처리 및 컨텍스트 매니저 생성 시기를 아는 방법에 대해 배워보자.

def copy(src, dst):
"""한 파일의 내용을 복사해서 다른 파일로 붙여넣는다.

Args:
	src (str): 복사할 내용이 있는 파일
	dst (str): 붙여넣을 새 파일
"""
	with open(src) as f_src:
		contents = f_src.read()
	
	with open(dst, 'w') as f_dst:
		f_dst.write(contents)

파일이 굉장히 크다면 다 읽을 때까지 기다려야한다. 중간에 뻗어버릴수도 있음...

이상적인 방법은 중첩 컨텍스트를 사용하는 것이다.

이 예제에서는 파일을 열고 한 줄씩 읽어서 바로바로 쓰도록 구현하는 것이 좋다.

def copy(src, dst):
"""한 파일의 내용을 복사해서 다른 파일로 붙여넣는다.

Args:
	src (str): 복사할 내용이 있는 파일
	dst (str): 붙여넣을 새 파일
"""
	with open(src) as f_src:
		with open(dst, 'w') as f_dst:
			for line in f_src:
				f_dst.write(line)

에러 다루기

파이썬에서 에러 다루는 방법은 try, except, finally를 사용하는 것이다.

try:
	에러가 발생할 수 있는 코드
except:
	에러 발생 시 수행
finally:
	예외 발생 여부 상관없이 실행됨

예를 들어보자.

프린터로 출력을 수행하는 코드가 있다.

doc 변수는 text 키를 갖는 딕셔너리 변수이다.

get_printer 컨텍스트 매니저 함수를 통해 doc['txt']를 출력하고자 한다.

doc 에는 txt 키가 없기 때문에 에러가 발생할 것이다.

error가 발생하고 끝나버리기 때문에 p.disconnect()가 호출되지 않아 다른 사람이 프린터를 사용할 수 없게된다.

def get_printer(ip):
	p = connect_to_printer(ip)

	yield

	p.disconnect()
	print('disconnected from printer')

doc = {'text' : 'This is my text.'}

with get_printer('10.0.34.111') as printer:
	printer.print_page(doc['txt'])

에러가 발생하던 말던 프린터 연결 해제는 수행되어야 하기 때문에 finally 부분에 작성하도록한다.

def get_printer(ip):
	p = connect_to_printer(ip)

	try:
		yield
	finally:
		p.disconnect()
		print('disconnected from printer')

doc = {'text' : 'This is my text.'}

with get_printer('10.0.34.111') as printer:
	printer.print_page(doc['txt'])

컨텍스트 매니저는 아래와 같은 패턴을 수행하는 경우가 많다.

연결하고 해제하고, 열고 닫고...