shelve模块
python 专有的序列化模块 只针对文件,用来持久化任意的Python对象
感觉比pickle用起来更简单一些,它也是一个用来持久化Python对象的简单工具。当我们写程序的时候如果不想用关系数据库那么重量级的东东去存储数据,不妨可以试试用shelve。shelf也是用key来访问的,使用起来和字典类似。shelve其实用anydbm去创建DB并且管理持久化对象的。shelve只有一个open方法。
创建一个新的shelve,直接使用shelve.open()就可以创建了,可以直接存入数据。
importshelve
s= shelve.open('test_shelve.db') #打开文件
try:
s['key'] = {'int': 9, 'float': 10.0, 'string': 'happy'}#直接对文件句柄操作,就可以存入数据
finally:
s.close()
shelve的创建
如果要再次访问
importshelve
s= shelve.open('test_shelve.db')try:
existing= s['key']#取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
finally:
s.close()print(existing)
访问shelve
dbm这个模块有个限制,它不支持多个应用同一时间往同一个DB进行写操作。所以当我们知道我们的应用如果只进行读操作,我们可以让shelve通过只读方式打开DB:
importshelve
s= shelve.open('test_shelve.db', flag='r')try:
existing= s['key']finally:
s.close()print(existing)
设置只读模式
当我们的程序试图去修改一个以只读方式打开的DB时,将会抛一个访问错误的异常。异常的具体类型取决于anydbm这个模块在创建DB时所选用的DB。
写回(Write-back)
由于shelve在默认情况下是不会记录待持久化对象的任何修改的,所以我们在shelve.open()时候需要修改默认参数,否则对象的修改不会保存。
importshelve
s= shelve.open('test_shelve.db')try:print(s['key'])
s['key']['new_value'] = 'this was not here before'
finally:
s.close()
s= shelve.open('test_shelve.db', writeback=True)try:print(s['key'])finally:
s.close()
写回writeback
上面这个例子中,由于一开始我们使用了缺省参数shelve.open()了,因此第6行修改的值即使我们s.close()也不会被保存。
所以当我们试图让shelve去自动捕获对象的变化,我们应该在打开shelf的时候将writeback设置为True。当我们将writeback这个flag设置为True以后,shelf将会将所有从DB中读取的对象存放到一个内存缓存。当我们close()打开的shelf的时候,缓存中所有的对象会被重新写入DB。
下面这个例子就能实现写入了
importshelve
s= shelve.open('test_shelve.db', writeback=True)try:print(s['key'])
s['key']['new_value'] = 'this was not here before'
finally:
s.close()
s= shelve.open('test_shelve.db', writeback=True)try:print(s['key'])finally:
s.close()
写回的正确方式
writeback方式有优点也有缺点。优点是减少了我们出错的概率,并且让对象的持久化对用户更加的透明了;但这种方式并不是所有的情况下都需要,首先,使用writeback以后,shelf在open()的时候会增加额外的内存消耗,并且当DB在close()的时候会将缓存中的每一个对象都写入到DB,这也会带来额外的等待时间。因为shelve没有办法知道缓存中哪些对象修改了,哪些对象没有修改,因此所有的对象都会被写入。
在shelve中,不能修改原有的数据结构类型,只能去覆盖原来的
importshelve
f= shelve.open('shelve_file', flag='r')#f['key']['int'] = 50 # 不能修改已有结构中的值#f['key']['new'] = 'new' # 不能在已有的结构中添加新的项
f['key'] = 'new' #但是可以覆盖原来的结构
f.close()
覆盖原来的结构
复杂的例子来一个
importtimeimportdatetimeimportshelveimporthashlib
LOGIN_TIME_OUT= 60db= shelve.open('user_shelve.db', writeback=True)defnewuser():globaldb
prompt= "login desired:"
whileTrue:
name=input(prompt)if name indb:
prompt= "name taken, try another:"
continue
elif len(name) ==0:
prompt= "name should not be empty, try another:"
continue
else:breakpwd= input("password:")
db[name]= {"password": hashlib.md5(pwd), "last_login_time": time.time()}#print '-->', db
defolduser():globaldb
name= input("login:")
pwd= input("password:")try:
password= db.get(name).get('password')exceptAttributeError:print("\033[1;31;40mUsername '%s' doesn't existed\033[0m" %name)return
if md5_digest(pwd) ==password:
login_time=time.time()
last_login_time= db.get(name).get('last_login_time')if login_time - last_login_time \033[0m"
%datetime.datetime.fromtimestamp(last_login_time).isoformat())
db[name]['last_login_time'] =login_timeprint("\033[1;32;40mwelcome back\033[0m", name)else:print("\033[1;31;40mlogin incorrect\033[0m")defmd5_digest(plain_pass):returnhashlib.md5(plain_pass).hexdigest()defshowmenu():#print '>>>', db
globaldb
prompt= """(N)ew User Login
(E)xisting User Login
(Q)uit
Enter choice:"""done=Falsewhile notdone:
chosen=Falsewhile notchosen:try:
choice=input(prompt).strip()[0].lower()except(EOFError, KeyboardInterrupt):
choice= "q"
print ("\nYou picked: [%s]" %choice)if choice not in "neq":print ("invalid option, try again")else:
chosen=Trueif choice == "q": done =Trueif choice == "n": newuser()if choice == "e": olduser()
db.close()if __name__ == "__main__":
showmenu()
View Code