zoukankan      html  css  js  c++  java
  • 序列化 json、pickle、shelve

    1、pickle和JSON序列化

    如果我们想用文本文件保存一个 Python 对象怎么操作?
    这里就涉及到序列化的问题,序列化指的是将原本的字典、列表等内容转换成一个字符串的过程。

    2、为什么要使用序列化

    比如,我们在python代码中计算的一个数据需要给另外一段程序使用,那我们怎么给?
    现在我们能想到的方法就是存在文件里,然后另一个python程序再从文件里读出来。
    但是我们都知道,对于文件来说是没有字典这个概念的,所以我们只能将数据转换成字典放到文件中。
    你一定会问,将字典转换成一个字符串很简单,就是str(dic)就可以办到了,为什么我们还要学习序列化模块呢?
    没错序列化的过程就是从dic 变成str(dic)的过程。现在你可以通过str(dic),将一个名为dic的字典转换成一个字符串,
    但是你要怎么把一个字符串转换成字典呢?
    聪明的你肯定想到了eval(),如果我们将一个字符串类型的字典str_dic传给eval,就会得到一个返回的字典类型了。
    eval()函数十分强大,但是eval是做什么的?e官方demo解释为:将字符串str当成有效的表达式来求值并返回计算结果。
    BUT!强大的函数有代价。安全性是其最大的缺点。
    想象一下,如果我们从文件中读出的不是一个数据结构,而是一句"删除文件"类似的破坏性语句,那么后果实在不堪设设想。
    而使用eval就要担这个风险。
    所以,我们并不推荐用eval方法来进行反序列化操作(将str转换成python中的数据结构)

    3、序列化的目的

    1、以某种存储形式使自定义对象持久化。
    2、将对象从一个地方传递到另一个地方。
    3、使程序更具维护性。

    Python中最常用两种方式进行序列化:
    JSON格式
    pickle模块

    4、JSON

    JSON(JavaScript Object Notation, JS 对象标记)是一种轻量级的数据交换格式。
    JSON 格式在互联网应用开发中应用非常广泛,可以作为不同的服务组件之间进行数据传递的格式。在互联网应用提供的各种 API 接口返回值基本都是 JSON 格式。
    Python 也提供了 json 模块支持 JSON 序列化。

    Json模块提供了四个功能:dumps、dump、loads、load
    dumps loads 用于网络传输
    dump load 用于文件写入和读取

    4.1、dumps、loads 用于对字符串进行操作
    dumps 和 loads 分别执行了序列化和反序列化的操作,并且 JSON 序列化后的内容为字符串,所以文本写入和读取不需要用二进制格式。

    import json
    dic = {'name': 'Tom', 'age': 25, 'sex': ''}
    ret = json.dumps(dic, ensure_ascii = True)  #序列化过程,就是变成一个特殊的字符串
    print(ret, type(ret))
    # 结果:{"name": "Tom", "age": 25, "sex": "man"} <class 'str'>
    # json.dumps里面ensure_ascii默认值为True,如果有汉字会显示成"sex": "u7537",改成False就可以
    #注意,json转换完的字符串类型的字典中的字符串是由双引号""表示的
    
    response = json.loads(ret)
    print(response, type(response))
    #反序列化过程,将json的字符串类型转换成字典格式
    #结果:{'name': 'Tom', 'age': 25, 'sex': 'man'} <class 'dict'>
    #注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由双引号""表示
    
    

    dic = {'name': 'Tom', 'age': 25, 'sex': '男'}
    ret2 = json.dumps(dic, separators=(',', '|'), ensure_ascii=False) 用,分隔键值对,用 | 分隔key和value
    print(ret2)
    # 结果:{"name"|"Tom","age"|25,"sex"|"男"}

    list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}] #也可以处理嵌套的数据类型
    str_dic = json.dumps(list_dic)
    print(type(str_dic), str_dic)
    # 结果:<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
    list_dic2 = json.loads(str_dic)
    print(type(list_dic2), list_dic2)
    # <class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]

    ValueError: Invalid control character at: line 1 column 122(char 123)
    出现错误的原因是字符串中包含了回车符( )或者换行符( )

    解决方案:
    
    方式一、转义
    json_data = json_data.replace('
    ', '\r').replace('
    ', '\n')
    
    方式二、使用关键字strict
    json.loads(json_data, strict=False)
    如果strict为False(默认值为True),则字符串中允许使用控制字符。此上下文中的控制字符是那些字符代码在0–31范围内的字符,包括“	”(制表符)、“
    ”、“r”和“”。

    4.2、dump、load  用于对文件进行操作

    import json
    dic = {'k1':'v1','k2':'v2','k3':'v3'}
    with open('json_file', 'w',encoding='utf-8') as f1:
        json.dump(dic, f1)
    #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
    
    
    with open('json_file', 'r', encoding='utf-8') as f1:
        dic2 = json.load(f1)  #load方法接入一个文件句柄,直接将文件中的json字符串转换成数据结构返回
    print(dic2)
    # {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
    
    import json
    data = {'username':['李华','二愣子'],'sex':'male','age':16}
    json_dict = json.dumps(data, sort_keys=True, indent=2, separators=(',', ':'), ensure_ascii=False)
    print(json_dict)
    # {
    #   "age":16,
    #   "sex":"male",
    #   "username":[
    #     "李华",
    #     "二愣子"
    #   ]
    # }

    Skipkeys:默认值是False,如果dict的keys内的数据不是python的基本类型(str,unicode,int,long,float,bool,None),设置为False时,就会报TypeError的错误。此时设置成True,则会跳过这类key

    ensure_ascii:默认值True,如果dict内含有non-ASCII的字符,则会类似uXXXX的显示数据,设置成False后,就能正常显示
    indent:应该是一个非负的整型,如果值为None,则一行显示数据,否则会换行且按照indent的数量显示前面的空白,这样打印出来的json数据也叫pretty-printed json
    separators:分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。
    encoding:默认是UTF-8,设置json数据的编码方式。
    sort_keys:将数据根据keys的值进行排序。

    json_dict = json.dumps(data, sort_keys=True, indent=None, separators=(',', '|'), ensure_ascii=False)
    print(json_dict)
    结果:{"age"|16,"sex"|"male","username"|["李华","二愣子"]}

    json写入文件时如何写入中文?

    在dumps时加入ensure_ascii=False
    ensure_ascii:,当它为True的时候,所有非ASCII码字符显示为uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的中文即可正常显示。)
    在打开文本时加入encoding='utf-8'

    Json 适用于所有语言
    在python中适用于所有语言的类型有以下这些,python中的集合不在这里

    +----------------+---------------+
    | Python   | JSON    |
    +=========+=========+
    | dict         | object   |
    +----------------+---------------+
    | list, tuple   | array   |
    +----------------+---------------+
    | str        | string |
    +----------------+---------------+
    | int, float    | number |
    +----------------+---------------+
    | True       | true |
    +----------------+---------------+
    | False           | false |
    +----------------+---------------+
    | None        | null |
    +----------------+---------------+

    5、pickle模块

    我们首先通过一个实例将 Python 的一个字典存入到文件中并读取出来恢复成字典对象,这个过程中用的就是 pickle 模块:
    pickle 只用于python语言之间的传输,包含所有的python支持的数据类型
    pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load(不仅可以序列化字典,列表...可以把python中任意的数据类型序列化)

    import pickle
    dic = {'k1':'v1','k2':'v2','k3':'v3'}
    str_dic = pickle.dumps(dic)
    print(str_dic)
    # b'x80x03}qx00(Xx02x00x00x00k1qx01Xx02x00x00x00v1qx02Xx02x00x00x00k2qx03Xx02x00x00x00v2qx04Xx02x00x00x00k3qx05Xx02x00x00x00v3qx06u.'
    
    dic2 = pickle.loads(str_dic)
    print(dic2)
    # {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}

    注意写入和读取文件都需要使用b二进制模式。
    最终我们写入文件并读取后仍然可以恢复到原来的字典对象。如果只是想将对象序列化成一个字节流,那可以使用 pickle.dumps(obj)。

    import pickle
    import time
    
    struct_time = time.localtime(100)
    print(struct_time)
    # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=1, tm_sec=40, tm_wday=3, tm_yday=1, tm_isdst=0)
    
    f = open('pickle_file', 'wb')
    pickle.dump(struct_time, f)
    f.close()
    
    f = open('pickle_file', 'rb')
    struct_time2 = pickle.load(f)
    print(struct_time2)
    # time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=8, tm_min=1, tm_sec=40, tm_wday=3, tm_yday=1, tm_isdst=0)

    json是一种所有的语言都可以识别的数据结构。
    如果我们将一个字典或者序列化成了一个json存在文件里,那么java代码或者js代码也可以拿来用。
    但是如果我们用pickle进行序列化,其他语言就不能读懂这是什么了~
    所以,如果你序列化的内容是列表或者字典,我们非常推荐你使用json模块
    但如果出于某种原因你不得不序列化其他的数据类型,而未来你还会用python对这个数据进行反序列化的话,那么就可以使用pickle

    6、shelve模块

    shelve也是python提供给我们的序列化工具,比pickle用起来更简单一些。
    shelve只提供给我们一个open方法,是用key来访问的,使用起来和字典类似。

    import shelve
    f = shelve.open('shelve_file')
    f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'}  #直接对文件句柄操作,就可以存入数据
    f.close()
    
    import shelve
    f1 = shelve.open('shelve_file')
    existing = f1['key']  #取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
    f1.close()
    print(existing)

    这个模块有个限制,它不支持多个应用同一时间往同一个DB进行写操作。所以当我们知道我们的应用如果只进行读操作,我们可以让shelve通过只读方式打开DB

    import shelve
    f = shelve.open('shelve_file', flag='r')
    existing = f['key']
    f.close()
    print(existing)

    由于shelve在默认情况下是不会记录待持久化对象的任何修改的,所以我们在shelve.open()时候需要修改默认参数,否则对象的修改不会保存。

    import shelve
    f1 = shelve.open('shelve_file')
    print(f1['key'])
    f1['key']['new_value'] = 'this was not here before'
    f1.close()
    
    f2 = shelve.open('shelve_file', writeback=True)
    print(f2['key'])
    f2['key']['new_value'] = 'this was not here before'
    f2.close()

    writeback方式有优点也有缺点。

    优点是减少了我们出错的概率,并且让对象的持久化对用户更加的透明了
    但这种方式并不是所有的情况下都需要,首先,使用writeback以后,shelve在open()的时候会增加额外的内存消耗,并且当DB在close()的时候会将缓存中的每一个对象都写入到DB,这也会带来额外的等待时间。因为shelve没有办法知道缓存中哪些对象修改了,哪些对象没有修改,因此所有的对象都会被写入。

  • 相关阅读:
    NetSuite Batch Process Status
    NetSuite generated bank files and the Bank Reconciliation process
    Global Search file content in Gitlab repositories, search across repositories, search across all files
    FedEx Package Rate Integration with NetSuite direct integrate by WebServices
    git Merge branches
    git tag and NetSuite deployment tracking
    API 读写cookie的方法
    C# 生成缩略图
    解决jquery操作checkbox全选全不选无法勾选问题
    JS读取写入删除Cookie方法
  • 原文地址:https://www.cnblogs.com/dxnui119/p/10000335.html
Copyright © 2011-2022 走看看