`shelve` — Python 객체 영속화
shelve — Python 객체 영속화
"shelf"는 영속적인, 사전(dict) 같은 객체예요. "dbm" 데이터베이스와의 차이는 shelf 안의 값(키가 아니라!)이 본질적으로 임의의 Python 객체일 수 있다는 점이에요. pickle 모듈이 다룰 수 있는 것이면 무엇이든요. 여기에는 대부분의 클래스 인스턴스, 재귀적 데이터 타입, 많은 공유 하위 객체를 담은 객체가 포함돼요. 키는 평범한 문자열이에요.
출처: Python 표준 라이브러리
본문
shelve.open(*filename*, *flag='c'*, *protocol=None*, *writeback=False*)
영속 사전을 엽니다. 지정한 filename은 기본 데이터베이스의 기본 파일 이름이에요. 부작용으로 확장자가 파일 이름에 추가될 수 있고 둘 이상의 파일이 만들어질 수 있어요. 기본적으로 기본 데이터베이스 파일은 읽기·쓰기용으로 열려요. 선택 flag 인자는 dbm.open()의 flag 인자와 같은 의미예요.
기본적으로 pickle.DEFAULT_PROTOCOL로 만든 피클이 값을 직렬화하는 데 사용돼요. pickle 프로토콜 버전은 protocol 인자로 지정할 수 있어요.
Python 의미론 때문에 shelf는 가변 영속 사전 항목이 언제 수정되는지 알 수 없어요. 기본적으로 수정된 객체는 shelf에 할당될 때에만 쓰여져요(Example 참고). 선택 writeback 인자를 True로 설정하면 접근한 모든 항목이 메모리에 캐시되고 sync()와 close() 때 다시 쓰여져요. 이렇게 하면 영속 사전의 가변 항목을 변경하는 게 더 편리해질 수 있지만, 많은 항목에 접근하면 캐시가 엄청난 메모리를 소비할 수 있고, 접근한 모든 항목을 다시 쓰기 때문에 close 연산이 매우 느려질 수 있어요(접근한 항목 중 어느 것이 가변이고 실제로 변경됐는지 판별할 방법이 없어요).
- 버전 3.10 변경:
pickle.DEFAULT_PROTOCOL이 이제 기본 pickle 프로토콜로 사용됨. - 버전 3.11 변경: filename에 path-like 객체를 받게 됨.
참고
shelf가 자동으로 닫히는 것에 의존하지 마세요. 더 이상 필요 없으면 항상
close()를 명시적으로 호출하거나,shelve.open()을 컨텍스트 관리자로 쓰세요:
with shelve.open('spam') as db:
db['eggs'] = 'eggs'
경고
shelve모듈은pickle로 뒷받침되기 때문에, 신뢰할 수 없는 소스에서 shelf를 불러오는 것은 안전하지 않아요. pickle과 마찬가지로 shelf를 불러오면 임의 코드가 실행될 수 있어요.
Shelf 객체는 사전이 지원하는 대부분의 메서드와 연산을 지원해요(복사, 생성자, |와 |= 연산자 제외). 이는 사전 기반 스크립트에서 영속 저장이 필요한 스크립트로의 전환을 쉽게 해줘요.
두 개의 추가 메서드가 지원돼요.
Shelf.sync()
shelf가 writeback=True로 열렸다면 캐시에 있는 모든 항목을 다시 써요. 가능하면 캐시를 비우고 영속 사전을 디스크에 동기화해요. 이는 shelf가 close()로 닫힐 때 자동으로 호출돼요.
Shelf.close()
영속 dict 객체를 동기화하고 닫아요. 닫힌 shelf에 대한 연산은 ValueError로 실패해요.
제약 (Restrictions)
-
어떤 데이터베이스 패키지를 사용할지(예:
dbm.ndbm또는dbm.gnu)는 어떤 인터페이스가 사용 가능한지에 따라 달라져요. 따라서dbm을 직접 써서 데이터베이스를 여는 것은 안전하지 않아요. 또한 (아쉽게도)dbm을 쓰면dbm의 한계에 영향을 받아요. 즉 데이터베이스에 저장된 객체의 (피클된 표현)은 꽤 작아야 하고, 드물게 키 충돌로 데이터베이스가 갱신을 거부할 수 있어요. -
shelve모듈은 shelve 객체에 대한 동시 읽기/쓰기 접근을 지원하지 않아요. (여러 동시 읽기 접근은 안전해요.) 프로그램이 쓰기용으로 shelf를 열어 두고 있으면, 다른 프로그램이 그것을 읽기나 쓰기용으로 열면 안 돼요. Unix 파일 잠금으로 이 문제를 해결할 수 있지만, 이는 Unix 버전마다 다르고 사용하는 데이터베이스 구현에 대한 지식을 요구해요. -
macOS에서
dbm.ndbm은 갱신 시 데이터베이스 파일을 조용히 손상시킬 수 있는데, 이는 데이터베이스에서 읽으려 할 때 심각한 충돌을 일으킬 수 있어요.
class shelve.Shelf(*dict*, *protocol=None*, *writeback=False*, *keyencoding='utf-8'*)
collections.abc.MutableMapping의 하위 클래스로, 피클된 값을 dict 객체에 저장해요.
기본적으로 pickle.DEFAULT_PROTOCOL로 만든 피클이 값을 직렬화하는 데 사용돼요. pickle 프로토콜 버전은 protocol 인자로 지정할 수 있어요. pickle 프로토콜에 대한 논의는 pickle 문서를 참고하세요.
writeback 인자가 True면 객체는 접근한 모든 항목의 캐시를 보관하고 sync와 close 시점에 dict에 다시 써요. 이는 가변 항목에 대한 자연스러운 연산을 허용하지만, 훨씬 더 많은 메모리를 소비하고 sync와 close가 오래 걸리게 할 수 있어요.
keyencoding 인자는 키가 기본 dict와 함께 사용되기 전에 인코딩하는 데 쓰는 인코딩이에요.
Shelf 객체는 컨텍스트 관리자로도 쓸 수 있는데, 그 경우 with 블록이 끝나면 자동으로 닫혀요.
- 버전 3.2 변경:
keyencoding인자 추가; 이전에는 키가 항상 UTF-8로 인코딩됐음. - 버전 3.4 변경: 컨텍스트 관리자 지원 추가.
- 버전 3.10 변경:
pickle.DEFAULT_PROTOCOL이 기본 pickle 프로토콜로 사용됨.
class shelve.BsdDbShelf(*dict*, *protocol=None*, *writeback=False*, *keyencoding='utf-8'*)
Shelf의 하위 클래스로, first(), next(), previous(), last(), set_location() 메서드를 노출해요. 이 메서드들은 pybsddb의 서드파티 bsddb 모듈에서 쓸 수 있지만 다른 데이터베이스 모듈에는 없어요. 생성자에 넘기는 dict 객체는 그 메서드들을 지원해야 해요. 이는 일반적으로 bsddb.hashopen(), bsddb.btopen(), 또는 bsddb.rnopen() 중 하나를 호출해 이루어져요. 선택 protocol, writeback, keyencoding 인자는 Shelf 클래스와 같은 의미예요.
class shelve.DbfilenameShelf(*filename*, *flag='c'*, *protocol=None*, *writeback=False*)
Shelf의 하위 클래스로, dict 같은 객체 대신 filename을 받아요. 기본 파일은 dbm.open()으로 열려요. 기본적으로 파일은 만들어지고 읽기·쓰기 모두로 열려요. 선택 flag 인자는 open() 함수와 같은 의미예요. 선택 protocol과 writeback 인자는 Shelf 클래스와 같은 의미예요.
예시 (Example)
인터페이스를 요약하면(key는 문자열, data는 임의 객체):
import shelve
d = shelve.open(filename) # open -- file may get suffix added by low-level
# library
d[key] = data # store data at key (overwrites old data if
# using an existing key)
data = d[key] # retrieve a COPY of data at key (raise KeyError
# if no such key)
del d[key] # delete data stored at key (raises KeyError
# if no such key)
flag = key in d # true if the key exists
klist = list(d.keys()) # a list of all existing keys (slow!)
# as d was opened WITHOUT writeback=True, beware:
d['xx'] = [0, 1, 2] # this works as expected, but...
d['xx'].append(3) # *this doesn't!* -- d['xx'] is STILL [0, 1, 2]!
# having opened d without writeback=True, you need to code carefully:
temp = d['xx'] # extracts the copy
temp.append(5) # mutates the copy
d['xx'] = temp # stores the copy right back, to persist it
# or, d=shelve.open(filename,writeback=True) would let you just code
# d['xx'].append(5) and have it work as expected, BUT it would also
# consume more memory and make the d.close() operation slower.
d.close() # close it
더 알아보기
dbm모듈:dbm스타일 데이터베이스에 대한 일반 인터페이스.pickle모듈:shelve가 사용하는 객체 직렬화.- 널리 지원되는 저장 형식을 갖고 네이티브 사전의 속도를 지닌 Persistent dictionary recipe.