序列化模块
序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化。
说明:
(1) 能存储在文件中的一定是字符串 或者是 字节
(2)能在网络上传输的 只有字节
由字典 dic --> 字符串的这个过程是序列化
由 字符串 --> dic的这个过程是反序列化
序列化 == 创造一个序列 ==》创造一个字符串
实例化 == 创造一个实例
序列化的目的
1、以某种存储形式使自定义对象持久化;
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性。
python中的序列化模块
(1)json 所有的编程语言都通用的序列化格式
# 它支持的数据类型非常有限 数字 字符串 列表 字典
(2)pickle 只能在python语言的程序之间传递数据用的
# pickle支持python中所有的数据类型
(3)shelve python3.* 之后才有的
json
Json模块提供了四个功能:dumps、dump、loads、load
loads和dumps
import json
#dumps序列化 loads反序列化 只在内存中操作数据 主要用于网络传输 和多个数据与文件打交道
#一个字典进行序列化
dic = {'happy': ('开心', 666)}
#序列化
ret = json.dumps(dic, ensure_ascii=False)
print(type(dic), dic)
print(type(ret), ret)
#反序列化
res = json.loads(ret)
print(type(res), res)
load和dump
#dump序列化 load反序列化 主要用于一个数据直接存在文件里—— 直接和文件打交道
import json
dic1 = {'happy': ('开心', 666)}
f = open('happy', 'w', encoding='utf-8')
json.dump(dic1, f, ensure_ascii=False) # 先接收要序列化的对象 再接受文件句柄
f.close()
#loads读取文件
f = open('happy', 'r', encoding='utf-8')
ret = json.load(f)
print(type(ret), ret)
带参数
#dumps带参数的使用
data = {'username': ['李华', '聪明的孩子'], 'sex': 'male', 'age': 16}
json_dic2 = json.dumps(data,sort_keys=True,indent=4,separators=(',',':'),ensure_ascii=False)
print(json_dic2)
参数说明:
Serialize obj to a JSON formatted str.(字符串表示的json对象)
Skipkeys:默认值是False,如果dict的keys内的数据不是python的基本类型(str,unicode,int,long,float,bool,None),设置为False时,就会报TypeError的错误。此时设置成True,则会跳过这类key
ensure_ascii:,当它为True的时候,所有非ASCII码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的中文即可正常显示。)
If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse).
If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity).
indent:应该是一个非负的整型,如果是0就是顶格分行显示,如果为空就是一行最紧凑显示,否则会换行且按照indent的数值显示前面的空白分行显示,这样打印出来的json数据也叫pretty-printed json
separators:分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。
default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError.
sort_keys:将数据根据keys的值进行排序。
To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.
多个字典进行序列化
import json
dic1 = {'happy': ('开心', 666)}
dic2 = {'miaomiao': ('cat', 123)}
dic3 = {'love': ('爱', 521)}
dic4 = {'heart': ('初心', 999)}
ret1 = json.dumps(dic1)
f = open('happy', 'w', encoding='utf-8')
f.write(ret1 + '\n')
ret2 = json.dumps(dic2)
f .write(ret2 + '\n')
ret3 = json.dumps(dic3)
f.write(ret3 + '\n')
ret4 = json.dumps(dic4)
f.write(ret4 + '\n')
f.close()
#从文件中读出多行序列化的内容
f = open('happy', 'r', encoding='utf-8')
for line in f:
print(json.loads(line.strip()))
f.close()
json 中的注意事项
json不支持元组 不支持除了str数据类型之外的key
import json
dic = {(190,90,'哈哈'):"luck"} # json不支持元组 不支持除了str数据类型之外的key
print(json.dumps(dic))
#报错
pickle
pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load (不仅可以序列化字典,列表…可以把python中任意的数据类型序列化)
dump 和 load
import pickle
dic2 = {'miaomiao': ('cat', 123)}
f = open('happy', 'wb' ) # 使用pickle dump必须以+b的形式打开文件
pickle.dump(dic2, f)
f.close()
f = open('happy', 'rb')
print(pickle.load(f))
f.close()
dumps 和 loads
import pickle
dic1 = {'happy': ('开心', 666)}
ret1 = pickle.dumps(dic1) # 序列化结果 不是一个可读的字符串 而是一个bytes类型
print(ret1)
print(pickle.loads(ret1))
关于写多行
import pickle
dic1 = {'happy': ('开心', 666)}
dic2 = {'miaomiao': ('cat', 123)}
dic3 = {'love': ('爱', 521)}
dic4 = {'heart': ('初心', 999)}
f = open('happy', 'wb')
pickle.dump(dic1, f)
pickle.dump(dic2, f)
pickle.dump(dic3, f)
pickle.dump(dic4, f)
f.close()
# 读写入的多行
f = open('happy', 'rb')
while True:
try:
print(pickle.load(f))
except EOFError:
break
json 与 pickle的区别
json,用于字符串 和 python数据类型间进行转换pickle,用于python特有的类型 和 python的数据类型间进行转换
json 在写入多次dump的时候 不能对应执行多次load来取出数据,pickle可以
json 如果要写入多个元素 可以先将元素dumps序列化,f.write(序列化+'\n')写入文件
读出元素的时候,应该先按行读文件,在使用loads将读出来的字符串转换成对应的数据类型
shelve
python 专有的序列化模块 只针对文件, 用来持久化任意的Python对象
感觉比pickle用起来更简单一些,它也是一个用来持久化Python对象的简单工具。当我们写程序的时候如果不想用关系数据库那么重量级的东东去存储数据,不妨可以试试用shelve。shelf也是用key来访问的,使用起来和字典类似。shelve其实用anydbm去创建DB并且管理持久化对象的。shelve只有一个open方法。
创建一个新的shelve,直接使用shelve.open()就可以创建了,可以直接存入数据。
import shelve
s = shelve.open('test_shelve.db') #打开文件
try:
s['key'] = {'int': 9, 'float': 10.0, 'string': 'happy'}#直接对文件句柄操作,就可以存入数据
finally:
s.close()
如果要再次访问
import shelve
s = shelve.open('test_shelve.db')
try:
existing = s['key']#取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
finally:
s.close()
print(existing)
dbm这个模块有个限制,它不支持多个应用同一时间往同一个DB进行写操作。所以当我们知道我们的应用如果只进行读操作,我们可以让shelve通过只读方式打开DB:
import shelve
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()时候需要修改默认参数,否则对象的修改不会保存。
import shelve
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()
上面这个例子中,由于一开始我们使用了缺省参数shelve.open()了,因此第6行修改的值即使我们s.close()也不会被保存。
所以当我们试图让shelve去自动捕获对象的变化,我们应该在打开shelf的时候将writeback设置为True。当我们将writeback这个flag设置为True以后,shelf将会将所有从DB中读取的对象存放到一个内存缓存。当我们close()打开的shelf的时候,缓存中所有的对象会被重新写入DB。
下面这个例子就能实现写入了
import shelve
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中,不能修改原有的数据结构类型,只能去覆盖原来的
import shelve
f = shelve.open('shelve_file', flag='r')
#f['key']['int'] = 50 # 不能修改已有结构中的值
# f['key']['new'] = 'new' # 不能在已有的结构中添加新的项
f['key'] = 'new' # 但是可以覆盖原来的结构
f.close()
复杂的例子来一个
import time
import datetime
import shelve
import hashlib
LOGIN_TIME_OUT = 60
db = shelve.open('user_shelve.db', writeback=True)
def newuser():
global db
prompt = "login desired: "
while True:
name = input(prompt)
if name in db:
prompt = "name taken, try another: "
continue
elif len(name) == 0:
prompt = "name should not be empty, try another: "
continue
else:
break
pwd = input("password: ")
db[name] = {"password": hashlib.md5(pwd), "last_login_time": time.time()}
#print '-->', db
def olduser():
global db
name = input("login: ")
pwd = input("password: ")
try:
password = db.get(name).get('password')
except AttributeError:
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 < LOGIN_TIME_OUT:
print("\033[1;31;40mYou already logged in at: <%s>\033[0m"
% datetime.datetime.fromtimestamp(last_login_time).isoformat())
db[name]['last_login_time'] = login_time
print("\033[1;32;40mwelcome back\033[0m", name)
else:
print("\033[1;31;40mlogin incorrect\033[0m")
def md5_digest(plain_pass):
return hashlib.md5(plain_pass).hexdigest()
def showmenu():
#print '>>>', db
global db
prompt = """
(N)ew User Login
(E)xisting User Login
(Q)uit
Enter choice: """
done = False
while not done:
chosen = False
while not chosen:
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 = True
if choice == "q": done = True
if choice == "n": newuser()
if choice == "e": olduser()
db.close()
if __name__ == "__main__":
showmenu()