티스토리 뷰

[file access]

f = open('mytextfile.txt', 'w', encoding='utf8')
for x in range(2, 10):
for y in range(2, 10):
f.write("{:02d} x {:02d} = {:02d}".format(x, y, x*y) + '\n')
f.write("\n"*2)
f.close()

 텍스트 파일 하나 열어서 구구단을 작성하는 예제이다. 


마지막에 항상 close 해줘야 한다. 


물론 현대 OS 들은 특정한 파일을 사용하던 핸들의 소유 프로세스가 


종료되면 자동으로 해당 리소스를 회수할 수 있지만, 프로그램의 실행 수명이 긴 상황에서


파일 핸들을 닫지 않으면 다른 프로세스에서 해당 파일을 엑세스 하지 못하는 경우가 있다.



[with]


파이썬의 파일 핸들러는 내부적으로 context manager 프로토콜을 따르도록 설계되어 있다.


따라서 다음과 같이 with 구문을 통해 엑세스 할 수 있다.


with open('mytextfile.txt', 'r', encoding='utf8') as f:
with open('copiedfile.txt', 'w', encoding='utf8') as g:
for line in f:
print(line)
g.write(line + '\n')

이 코드는 open() ~ FileHandle.close() 이르는 구간이 with 구문에 의한 블록으로 


감싸지고 명시적으로 열었던 파일 핸들을 닫는 코드를 작성하지 않고 있다.


이는 open() 함수를 통해서 생성되는 파일 핸들이 컨텍스트 매니저 프로토콜을 따르고 있기 때문이다.


컨텍스트 매니저 객체들은 with 문과 함께 사용 되었을 떄 다음과 같은 동작을 처리 할 수 있다.


1. 객체가 생성된 후 with 블럭에 진입하면서 미리 지정된 특정한 작업을 수행한다.


2. with 블럭을 떠나는 시점에 미리 지정된 특정한 작업을 수행한다.


보통의 경우에는 시작 시점에는 특정한 동작을 수행하는 일이 별로 없을 것이다.


파일 객체의 경우에 with 블럭을 떠나는 시점에 자신을 닫는 동작을 미리 수행하도록 정해저 있다.


따라서 with 문 내에서 예외가 발생해서 실행이 중단되거나, 함수 내에서 리턴하는 동작을 만나더라도


파일 객체는 닫기를 수행하는 기회를 보장받게 된다.


또한 with 문을 쓸 떄의 장점은 위의 예에서 보듯이 with 문을 중첩이 가능하다.


따라서 어느 한 지점에서 문제가 발생하여 모든 블럭을 빠저 나와야 하는 상황에서도 가장 하위의


블럭에서부터 파일을 순차적으로 닫고 안전하게 리소스를 회수할 수 있게 된다.


제어 로직에서의 전후관계를 따로 추적하지 않아도 그만큼 코드가 간결해지는 장점도 있다.



[context manager: 컨텍스트 매니저]


컨텍스트 매니저는 with 구문에 쓰일 수 있는 객체의 타입이며 컨텍스트 매니저는 다음 두개의 매소드를


정의하고 있는 것으로 간주한다.


__enter__(self) : with 문에 진입하는 시점에 자동으로 호출


__exit__(self, type, value, traceback) : with 문이 끝나기 직전에 자동으로 호출


__exit__ 매소드에서 받는 세개의 인자는 해당 객체와 연관된 컨텍스트 내에서


예외가 발생되었을 떄, 해당 예외에 관한 정보이다.





import sqlite3 as sql


class SQLiteManager:
def __init__(self, path=":memory:"):
self.con = sql.connect(path)

def __enter__(self):
print("Open Database.")
if self.con is not None:
self.cur = self.con.cursor()
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.cur.close()
self.con.close()
print("Closed Database.")

def create_table(self, table, *args):
fields = ("{}," * len(args)).format(*args)[:-1]
self.cur.execute("CREATE TABLE {}({})".format(table, fields))

def insert_fields(self, table, field_list):
self.cur.executemany("INSERT INTO {} Values(?, ?)".format(table), field_list)

def select_table(self, table, key=False):
self.cur.execute("select * from {}".format(table))
return [fd[0] for fd in self.cur.description] if key else [row for row in self.cur]

def commit(self):
self.con.commit()


with SQLiteManager("./test.db") as db:
db.create_table('phonebook1', 'name text', 'phone text')
db.create_table('phonebook2', 'name text', 'phone text')
db.create_table('phonebook3', 'name text', 'phone text')
db.insert_fields('phonebook1', [('lee', '011'), ('kim', '010')])
db.commit()
print(db.select_table('phonebook1', key=True))
print(db.select_table('phonebook1'))






댓글