python3__标准库__序列化json / packle / shelve 模块

1、序列化

1. 定义:把变量从内存中变成可存储或可传输的过程称之为序列化。在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。

2. 序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。

3. 把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。

4. python提供了pickle模块来实现序列化。

5. 序列化目的:

(1)便于存储。序列化过程将文本信息转变为二进制数据流。这样就信息就容易存储在硬盘之中,当需要读取文件的时候,从硬盘中读取数据,然后再将其反序列化便可以得到原始的数据。防止断电,内存中的数据丢失。

(2)便于传输。当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把這个对象转换为字节序列,在能在网络上传输;接收方则需要把字节序列在恢复为对象。

2、pickle模块

(1)基本说明

1. Pickle的问题和所有其他编程语言特有的序列化问题一样,就是pickle只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。

2. python3移除了cpickle模块

3. dumps()dump()函数一样执行的是序列化操作。不同的是dump()接收一个流对象并在文件中写入序列化后的数据,同时也接受待序列化的内存数据。

4. loads()load()函数一样执行的是反序列化操作。不同的是load()函数接收一个流对象并去文件读取序列化后的数据,返回内存中的对象。

5. pickle模块是以二进制的形式序列化后保存到文件中(保存文件的后缀为”.pkl”),不能直接打开进行预览。

# ! /usr/bin/env python
# coding:utf-8
# python interpreter:3.6.2
# author: admin_maxin
import pickle


obj = 123, "abcdedf", ["ac", 123], {"key": "value", "key1": "value1"}
print(type(obj), obj)                     # <class 'tuple'>
print("*" * 100)

# 1.序列化到文件
with open("a.txt", "wb+") as f:
    pickle.dump(obj, f)

with open("a.txt", "rb+") as f:
    c = pickle.load(f)
    print("new type:", type(c), "\n", c)  # <class 'tuple'>

# 2.序列化到内存(bytes格式保存),然后对象可以以任何方式处理如通过网络传输
obj1 = pickle.dumps(obj)
print(type(obj1), obj1)                   # <class 'bytes'>

c2 = pickle.loads(obj1)
print(type(c2), c2)                       # <class 'tuple'>

(2)序列化操作

pickle.dump(obj, file, protocol=None,*,fix_imports=True)

pickle.dumps(obj, protocol=None,*,fix_imports=True)

Pickler(file, protocol).dump(obj) == pickle.dump()

protocol参数说明:

        关于参数protocol,一共有5中不同的类型,即(0,1,2,3,4)。(0,1,2)对应的是python早期的版本,(3,4)则是在python3之后的版本。此外,参数可选 pickle.HIGHEST_PROTOCOL和pickle.DEFAULT_PROTOCOL。当前,python3.5版本中,pickle.HIGHEST_PROTOCOL的值为4,pickle.DEFAULT_PROTOCOL的值为3。当protocol参数为负数时,表示选择的参数是pickle.HIGHEST_PROTOCOL。

(3)反序列化操作

pickle.load(file, *,fix_imports=True, encoding=”ASCII”. errors=”strict”)

# ----------  读取的时候,参数protocol是自动选择的,load()方法中没有这个参数。

pickle.loads(bytes_object, *,fix_imports=True, encoding=”ASCII”. errors=”strict”)

pickle.Unpickler(file,*,fix_imports=True,encoding="ASCII",errors="strict") == pickle.load()

3、json模块

(1)基本说明

1. 如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。

2. JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。 

3. JSON标准规定JSON编码是UTF-8,所以我们总是能正确地在Python的str与JSON的字符串之间转换。

import json


# 1.序列化到内存
a = {"name": "maxin", "age": 23}
json_str = json.dumps(a)
print(type(json_str), json_str)                  # <class 'str'> {"name": "maxin", "age": 23}

target = json.loads(json_str)
print(type(target), target)                      # <class 'dict'> {'name': 'maxin', 'age': 23}
print("*" * 100)


# 2.序列化到文件
a1 = {"name": "maxin", "age": 23}
with open("a1.txt", "w+") as f:
    json.dump(a1, f)

with open("a1.txt", "r+") as f:
    json_str1 = json.load(f)
    print(type(json_str1), json_str1)            # <class 'dict'> {'name': 'maxin', 'age': 23}

(2)json进阶

        很多时候,我们更喜欢用class表示对象,比如定义Student类,然后序列化。但是此时我们会得到如下的错误提示:

提示类实例对象(s1)为不可序列化的json对象。

1. 因此可通过dump()或dumps()中的default__dict__参数进行类的序列化。

2. 因此可通过load()或loads()中的object_hook参数进行类的反序列化。

# -------------------------------------------------------------------------------------json进阶
import json


class Student(object):

    def __init__(self, name="", age=0, score=0):
        self.name = name
        self.age = age
        self.score = score


if "__main__" == __name__:

    s1 = Student("maxin", 23, 97)
    # json_str = json.dumps(s1)
    # print(json_str)

    # 1.序列化
    # default(obj)是一个应返回obj的可序列化版本或引发TypeError的函数。 默认情况下只会引发TypeError。
    json_str = json.dumps(s1, default=lambda obj: obj.__dict__)

    # <class 'str'> {"name": "maxin", "age": 23, "socre": 97}
    print(type(json_str), json_str)


    # 2.反序列化
    def dict2student(d):
        return Student(d['name'], d['age'], d['score'])

    # 将使用``object_hook``的返回值而不是``dict``。 此功能可用于实现自定义解码器(例如JSON-RPC类提示)。
    json_str_loads = json.loads(json_str, object_hook=dict2student)

    # <class '__main__.Student'> <__main__.Student object at 0x000001DACB081AC8>
    print(type(json_str_loads), json_str_loads)

4、python与json之间数据类型转换对照列表

(1)Python 序列化为 JSON 类型转换对应表

Python JSON
dict object
list, tuple array
str string
int, float, int- & float-derived Enums number
True true
False false
None null

(2)JSON 类型反序列化为python类型对应表

JSON Python
object dict
array list
string str
number (int) int
number (real) float
true True
false False
null None

5.shelve模块

shelve模块是一个简单的将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式。shelve对象是一个类字典的对象,可序列化多组数据,并通过key将不同的序列化数据分隔开,反序列化时就会非常的方便。另外,写程序的时候如果不想用关系数据库那么重量级的去存储数据,也可以用到shelve。需要注意:在shelve模块中key必须要是字符串

(1)源码说明

shelve模块当中仅提供了一个函数那就是open()函数。该函数的主要作用是:打开一个持久字典(persistent dictionary)方便后续进行读(read)和写(write)操作。

def open(filename, flag='c', protocol=None, writeback=False):
    """Open a persistent dictionary for reading and writing."""

    --filename: 文件名称
    --flag: r(只读)w(读写现存数据源)c(读写新的或现存的数据源)n(读写新的数据源)
    --protocol: 可选的protocol参数指定pickle协议的版本(0,1或2)。
    --writeback: 自主选择要不要做出修改后将拷贝写会

    """
    The filename parameter is the base filename for the underlying
    database.  As a side-effect, an extension may be added to the
    filename and more than one file may be created.  The optional flag
    parameter has the same interpretation as the flag parameter of
    dbm.open(). The optional protocol parameter specifies the
    version of the pickle protocol (0, 1, or 2).

    See the module's __doc__ string for an overview of the interface.
    """

    return DbfilenameShelf(filename, flag, protocol, writeback)

(2)数据序列化与反序列化

(a)源码中注释的部分说明:

To summarize the interface (key is a string, data is an arbitrary
object):
接口总结

        import shelve
        # 打开文件
        d = shelve.open(filename) # open, with (g)dbm filename -- no suffix
        # 按照键值对方式存储数据。key存在则覆盖值。
        d[key] = data   # store data at key (overwrites old data if
                        # using an existing key)
        # data存储的是键对应的值的一个拷贝
        data = d[key]   # retrieve a COPY of the data at key (raise
                        # KeyError if no such key) -- NOTE that this
                        # access returns a *copy* of the entry!
        del d[key]      # delete data stored at key (raises KeyError
                        # if no such key)
        flag = key in d # true if the key exists
        # 获取所有存在的键(效率非常的低下)
        list = d.keys() # a list of all existing keys (slow!)

        d.close()       # close it

(b)数据序列化

序列化会直接在当前python脚本目录下新建3个文件。

filename.bak: 生成的数据文件的备份文件

filename.dat: 数据文件(二进制)

filename.dir: CPS备份文件

import shelve


d = shelve.open("shelve_test")


class Test(object):
    def __init__(self, n=1):
        self.n = n


t = "this is the first"
t1 = [1, 2, 3]
t2 = (4, 5, 6)
t3 = set([7, 8, 9])
t4 = {"age": 24}
t5 = Test()
t5 = Test(11111)

try:
    d["t"] = t
    d["t1"] = t1
    d["t2"] = t2
    d["t3"] = t3
    d["t4"] = t4
    d["t5"] = t5
except KeyError as ke:
    exit("key error!")
finally:
    d.close()

(c)数据反序列化

此处强调一下:data = r["t4"],data引用的实际上是key(t4)对应数据的一份拷贝open()中参数writeback则可让你自主选择要不要做出修改后将拷贝写回。

import shelve


r = shelve.open("shelve_test")
print(r["t4"])
r.close()

(3)shelve模块的注意事项

(a)shelve模块不允许多个应用同时对一个DB(文件)进行写操作

import shelve


# python3.6中将文件处理模式设置成“只读”并不起作用
db = shelve.open("shelve_test", flag="r")

print(db.get("t1"))
print(db.get("t2"))
db["t"] = "this is update"
print(db.get("t"))
db.close()

(b)open()函数中“writeback=True”参数说明

import shelve


list1 = ["ma", "xiao", "xing"]
db1 = shelve.open("test")
db1["lis"] = list1                   # ①通过“直接赋值”的方式写入
db1["lis"].append("shui")            # ②
print(type(db1["lis"]), db1["lis"])  # ③<class 'list'> ['ma', 'xiao', 'xing']
db1.close()

# ------------------------------------------------------------------------------------------------------- #
# ②中key=lis对应的数据并没有写回
# 原因:当②中再次读取db1["lis"]的时候,db1["lis"]返回的实际上是原始数据源的一个copy,
#      对应的内存地址都不同。所以②中修改的内容并不会出现在下边的③中的db1["lis"],因为其
#      是原始数据源的一个全新的copy.
# ------------------------------------------------------------------------------------------------------- #

# ----------改进措施 1
import shelve

list2 = ["ma", "xiao", "xing"]
db2 = shelve.open('shelve_db2.dat')
db2["lis"] = list2
temp = db2["lis"]
temp.append("shui")
db2["lis"] = temp                      # 通过直接赋值,覆盖掉原有的值
print(type(db2["lis"]), db2["lis"])    # <class 'list'> ['ma', 'xiao', 'xing', 'shui']
db2.close()


# ----------改进措施 2
import shelve

list3 = ["ma", "xiao", "xing"]
db2 = shelve.open("test", writeback=True)
db2["lis"] = list3
db2["lis"].append("shuai")
print(type(db2["lis"]), db2["lis"])      # <class 'list'> ['ma', 'xiao', 'xing', 'shuai']
db2.close()

(c)字典键值对无法写回问题

import shelve


db3 = shelve.open("test")
dic = {"name": "maxiaoxing", "sex": "man"}
db3["dic"] = dic
db3["dic"]["age"] = 23

for k, v in db3.items():
    print(k, v)

db3.close()
# ------------------------------------------------------------------------------------------------------- #
# 打印结果显示:对字典内容的添加操作并没有写回,打印操作仅仅显示的是从原始数据的一个拷贝,
# 并不会显示增加的键值对,若将open()函数的“writeback=”设置为True则会写回成功。
# ------------------------------------------------------------------------------------------------------- #

(d)open()函数中writeback参数的优缺点

优点:①减少了出错的概率,且让对象的持久化对用户更加的透明

缺点:①writeback=True之后,会将从DB中读取的对象放入一个内存缓存中,耗内存

           ②当close()之后内存缓存中的对象会被重新写入DB,耗时

(4)shelve模块open()函数writeback参数的应用实例

模拟保存用户的登录状态:

用户距离上一次登录时间不超过设置的“最大登录间隔时间”则可以继续登录;

超过时间则无法使用原来的用户名和密码,需要重新进行注册。

代码来源:https://blog.csdn.net/u012145252/article/details/80028146?utm_source=copy

猜你喜欢

转载自blog.csdn.net/admin_maxin/article/details/82290035