• Python 常用模块(2) 序列化(pickle,shelve,json,configpaser)


    主要内容:
      一. 序列化概述
      二. pickle模块
      三. shelve模块
      四. json模块(重点!)
      五. configpaser模块

    一. 序列化概述
    1. 序列化: 将字典,列表等内容转换成一个字符串的过程就叫做序列化.

    2. 为什么要把其他数据类型转换成字符串?
    (1)能够在网络上传输的只能是bytes
    (2)能够存储在文件里的只有bytes和str

    3. 序列化的目的:
    (1)以某种存储形式使自定义对象持久化
    (2)将对象从一个地方传递到另一个地方
    (3)使程序更具维护性
    4. 反序列化:
    序列化: str --> 数据结构
    反序列化: str <-- 数据结构

    5. 在Python中序列化的三种方案:
    (1)pickle模块: 可以将Python中的任意数据类型转换成bytes并写入到文件中,同样也可以把文件中写好的bytes转换回Python的数据,这个过程被称为反序列化.
    (2)shelve模块: 它是一种简单另类的序列化方案,有一点类似于今后会学习的redis.它可以作为一种小型的数据库来使用.
    (3)json模块: 将python中常用的字典,列表转化成字符串.它是目前前后端数据交互使用频率最高的一种数据格式.


    二. pickle模块
    pickle是把python对象写入到文件中的一种解决方案.写入到文件中的是bytes,它不是给人看的,只有机器可以识别.
    pickle可以把python中任意的数据类型序列化.

    1. pickle模块中的dumps()方法和loads()方法
    注意: dumps()和loads()与文件操作无关

    举例说明:
    import pickle   # 引入模块
    class Cat:      # 创建类
        def __init__(self, name, age):
            self.name = name
            self.age = age
        def catch_mouse(self):
            print(self.name, "抓老鼠")
    
    cat = Cat("jerry", 18)    # 创建一个对象
    
    byte = pickle.dumps(cat)  # dumps()方法 --> 序列化一个对象
    print(byte)               # 打印结果是一堆很长的二进制字符串
    
    new_cat = pickle.loads(byte)    # 把二进制字符串反序列化为原来的对象
    new_cat.catch_mouse()           # 执行结果: jerry 抓老鼠 --> 反序列化之后得到的对象还是原来那个类型的对象
    View Code

    2. pickle模块中的dump()方法和load()方法
    注意: dump()方法和load()方法多用于文件的写入写出操作

    举例说明:
    import pickle
    class Cat:
        def __init__(self, name, age):
            self.name = name
            self.age = age
        def catch_mouse(self):
            print(self.name, "抓老鼠")
    
    cat = Cat("jerry", 18)      # 创建一个对象
    
    with open("cat", mode="wb") as f:
        pickle.dump(cat, f)     # 把对象cat以二进制字符串的形式写进文件中
    
    with open("cat", mode="rb") as f:
        new_cat = pickle.load(f)    # 从文件中读取信息,并把信息反序列化为对象
        new_cat.catch_mouse()       # 对象可以访问类中的方法
    View Code

    3. pickle模块还支持多个对象的写入写出

    举例说明:
    import pickle
    class Cat:
        def __init__(self, name, age):
            self.name = name
            self.age = age
        def catch_mouse(self):
            print(self.name, "抓老鼠")
    
    lst = [Cat("jerry", 19), Cat("tommy", 20), Cat("kendy", 21)]
    
    with open("cat", mode="wb") as f:
        for el in lst:
            pickle.dump(el, f)      # 把对象序列化并写入文件
    
    with open("cat", mode="rb") as f:
    for i in range(len(lst)):       # 我们可能事先不知道列表中到底有多少个对象
        new_cat = pickle.load(f)    # 把文件中的二进制反序列化为对象
        new_cat.catch_mouse()       # 对象访问catch_mouse()方法
    View Code

    以上操作是有问题的,因为我们在实际情况中是不知道文件内容中有多少个对象的.因此,我们需要换一种操作方式.

    举例说明1:
    import pickle
    class Cat:
        def __init__(self, name, age):
            self.name = name
            self.age = age
        def catch_mouse(self):
            print(self.name, "抓老鼠")
    
    lst = [Cat("jerry", 19), Cat("tommy", 20), Cat("cendy", 21)]
    
    with open("cat", mode="wb") as f:
        pickle.dump(lst, f)             # 直接把整个列表序列化并写进文件中
    
    
    with open("cat", mode="rb") as f:
        new_lst = pickle.load(f)        # 读取文件中的信息,并将其反序列化,拿到一个列表
        for el in new_lst:              # 遍历整个列表
            el.catch_mouse()            # 每一个元素都可以访问catch_mouse()方法
    View Code

    举例说明2:
    import pickle
    dic1 = {(1, 2, 3):{'a', 'b'}, 1:'abc'}
    dic2 = {(1, 2, 3):{'a', 'b'}, 2:'abc'}
    dic3 = {(1, 2, 3):{'a', 'b'}, 3:'abc'}
    dic4 = {(1, 2, 3):{'a', 'b'}, 4:'abc'}
    with open ("pickle_file", "wb") as f:
        pickle.dump(dic1, f)
        pickle.dump(dic2, f)
        pickle.dump(dic3, f)
        pickle.dump(dic4, f)
    
    # 第一种读取方式:
    with open("pickle_file", "rb") as f:
        ret = pickle.load(f)
        print(ret, type(ret))
        ret = pickle.load(f)
        print(ret, type(ret))
        ret = pickle.load(f)
        print(ret, type(ret))
        ret = pickle.load(f)
        print(ret, type(ret))
        # ret = pickle.load(f)    # EOFError: Ran out of input
        # print(ret, type(ret))   # 如果文件中的对象已经全部被load(拿出来反序列化)了,此时再次load就会报错
    
    # 以上代码执行结果:
    # {(1, 2, 3): {'b', 'a'}, 1: 'abc'} <class 'dict'>
    # {(1, 2, 3): {'b', 'a'}, 2: 'abc'} <class 'dict'>
    # {(1, 2, 3): {'b', 'a'}, 3: 'abc'} <class 'dict'>
    # {(1, 2, 3): {'b', 'a'}, 4: 'abc'} <class 'dict'>
    
    # 改进后的第二种读取方式:
    with open("pickle_file", "rb") as f:
        while True:
            try:
                ret = pickle.load(f)
                print(ret, type(ret))
            except EOFError:        # 异常被捕获了
                break
    View Code

    对pickle模块的总结:
    (1)pickle只能在python中使用,它只支持python这门语言,跨平台性较差.
    (2)pickle序列化支持在python中的几乎所有数据类型.
    (3)pickle中的dumps方法序列化的结果一定是字节.
    (4)在进行文件操作时,需要用rb和wb模式打开文件.
    (5)可以dump多个对象到文件中,也可以从文件中load出来多个对象(load次数超过对象个数会报错,注意捕获异常).


    三. shelve模块
    shelve提供python的持久化操作,即把数据写到硬盘上.
    shelve的操作方式与字典非常相似,可以把它看作是"文件的字典式操作".

    1. 增加
    import shelve
    helf = shelve.open("shelve_test1")
    # print(shelf["jay"])      # 报错,因为文件中还没有"jay"的信息
    
    shelf["jay"] = "周杰伦"    # 执行新增
    print(shelf["jay"])        # 打印结果: 周杰伦 --> 新增成功
    
    shelf.close()
    View Code

    2. 修改
    import shelve
    shelf = shelve.open("shelve_test1")
    print(shelf["jay"])
    # 像操作字典一样直接进行修改:
    shelf["jay"] = {"name": "周杰伦", "age": 18, "hobby": "唱歌"}
    print(shelf["jay"])
    shelf.close()
    # 执行结果:
    # 周杰伦
    # {'name': '周杰伦', 'age': 18, 'hobby': '唱歌'}
    
    尝试修改{'name': '周杰伦', 'age': 18, 'hobby': '唱歌'}这个字典中的内容:
    shelf = shelve.open("shelve_test1")
    shelf["jay"]["name"] = "王力宏"     # 尝试修改
    shelf.close()
    shelf = shelve.open("shelve_test1")
    print(shelf["jay"]["name"])         # 查看我们修改的内容
    shelf.close()
    # 打印结果: 周杰伦 --> 修改失败
    View Code

    解决方案如下:
    shelf = shelve.open("shelve_test1", writeback=True)
    shelf["jay"]["name"] = "王力宏"     # 再次尝试修改
    shelf.close()
    shelf = shelve.open("shelve_test1")
    print(shelf["jay"]["name"])         # 查看我们修改的内容
    shelf.close()
    # 打印结果: 王力宏 --> 修改成功
    View Code

    writeback=True可以动态地把我们修改的信息写入到文件中,而且它还可以删除数据,就像字典一样


    3. 删除
    shelf = shelve.open("shelve_test1", writeback=True)
    del shelf["jay"]
    shelf.close()
    shelf = shelve.open("shelve_test1")
    print(shelf["jay"])     # 打印结果: 报错 --> 因为之前已经把"jay"的数据给删除了
    shelf.close()
    View Code

    4. 查找
    shelf = shelve.open("shelve_test1", writeback=True)
    shelf["乐坛半壁江山"] = "汪峰"
    shelf["华仔"] = "刘德华"
    shelf["星爷"] = "周星驰"
    shelf.close()
    
    # 遍历文件拿到所有key
    shelf = shelve.open("shelve_test1")
    
    for k in shelf:
        print(k)        # 拿到所有key
    
    for k in shelf.keys():
        print(k)        # 拿到所有key
    
    for v in shelf.values():
        print(v)        # 拿到所有value
    
    for k, v in shelf.items():
        print(k, v)     # 拿到所有key和value
    
    shelf.close()
    View Code



    四. json模块
    json模块提供了四个功能: 序列化(dumps和dump), 反序列化(loads和load)

    如下实例:

    import json
    dic = {'key': 'value', 'key2': 'value2'}
    
    ret = json.dumps(dic)       # 序列化
    print(dic, type(dic))       # {'key': 'value', 'key2': 'value2'} <class 'dict'>
    print(ret, type(ret))       # {"key": "value", "key2": "value2"} <class 'str'>
    
    res = json.loads(ret)       # 反序列化
    print(res, type(res))       # {'key': 'value', 'key2': 'value2'} <class 'dict'>
    # json能够序列化的数据有什么特点?观察下面几个示例,分析结果.
    # 特点1: 字典的key是整型,经过序列化和反序列化之后变成了字符串类型
    # 特点2: 字典的value是元组, 经过序列化和反序列化之后变成了列表类型
    dic = {1:[1,2,3], 2:(4,5,'aa')}
    ret = json.dumps(dic)       # 序列化
    print(dic, type(dic))       # {1: [1, 2, 3], 2: (4, 5, 'aa')} <class 'dict'>
    print(ret, type(ret))       # {"1": [1, 2, 3], "2": [4, 5, "aa"]} <class 'str'>
    
    res = json.loads(ret)       # 反序列化
    print(res, type(res))       # {'1': [1, 2, 3], '2': [4, 5, 'aa']} <class 'dict'>
    
    # 特点3: set集合类型不能被json序列化
    # 特点4: 字典的键必须是字符串才能被json序列化
    s = {1, 2, "aaa"}
    json.dumps(s)               # 报错: TypeError: Object of type 'set' is not JSON serializable
    json.dumps({(1,2,3):123})   # 报错: TypeError: keys must be a string

    总结: json在所有的语言之间都通用:即在python中json序列化后的数据,把它拿到java中也可以反序列化,反之亦然.
    可以认为,json序列化后的数据,在其他语言中也能够反序列化回来,所有语言都可以识别"json序列化后的数据".
    由此也导致json能够处理的数据非常有限,只有 字符串,列表,字典,数字 这几种类型,而且字典中的key只能是字符串.

    # 向文件中写入字典
    import json
    dic = {'key' : 'value','key2' : 'value2'}
    ret = json.dumps(dic)   # 序列化(这里是将序列化结果全部写入内存,下面的代码再从内存中读取全部结果并写入文件)
    with open("json_file", "a") as f:
        f.write(ret)        # 从内存中读取数据,并写入文件中
    
    # 从文件中读取字典
    with open ("json_file", "r") as f:
        str_dic = f.read()          # 读取全部文件内容并将其写入内存
        dic = json.loads(str_dic)   # 将内存中的字符串反序列化
        print(dic)                  # 打印结果: {'key': 'value', 'key2': 'value2'}
    
    
    # dump和load是直接操作文件的,如下示例:
    dic = {'key1' : 'value1','key2' : 'value2'}
    with open('json_file', 'a') as f:
        json.dump(dic, f)   # 把dic序列化并写入文件json_file中
    
    with open('json_file', 'r') as f:
        dic = json.load(f)  # 把文件内容反序列化为字典
        print(dic)          # {'key1': 'value1', 'key2': 'value2'}

    总结: 如果我们是进行文件相关的操作(读/写),那么可以用dump和load.如果是处理网络上传输的数据是,由于此时数据都是在内存中,这就要用到dumps和loads了.

    # 特点5: 不支持连续的存取
    dic = {'key1':'value1', 'key2':'value2'}
    with open("json_file", "a") as f:
        json.dump(dic, f)
        json.dump(dic, f)
        json.dump(dic, f)
    
    with open("json_file", "r") as f:
        dic = json.load(f)
        print(dic.keys())

    总结: 上面程序中虽然成功通过dump多次向文件中存入3个字典,但是load会报错. 也就是说load只能读取"存一个字典"的文件,嵌套字典也可以,但最外层只能是一个.

    # 解决办法
    dic = {'key1':'value1', 'key2':'value2'}
    with open("json_file", "a") as f:
        str_dic = json.dumps(dic)
        f.write(str_dic + "
    ")
        str_dic = json.dumps(dic)
        f.write(str_dic + "
    ")
        str_dic = json.dumps(dic)
        f.write(str_dic + "
    ")
    
    with open("json_file", "r") as f:
        for line in f:
            dic = json.loads(line.strip())
            print(dic)
    
    # 执行结果:
    # {'key1': 'value1', 'key2': 'value2'}
    # {'key1': 'value1', 'key2': 'value2'}
    # {'key1': 'value1', 'key2': 'value2'}

    综上所述:
    json的dumps和loads -- 在内存中做数据转换:
    dumps(序列化) --> 数据类型 转成 字符串
        loads(反序列化) --> 字符串 转成 数据类型
    json的dump和load -- 直接将数据类型写入文件,直接从文件中读出数据类型:
    dump(序列化) --> 把数据类型序列化并写入文件
    load(反序列化) --> 从文件中读出内容并将其反序列化为数据类型

    json是所有语言都通用的一种序列化格式,只支持列表,字典,字符串,数字,并且字典的key必须是字符串.

    # ensure_ascii 关键字参数
    dic = {"key":"你好}
    print(json.dumps(dic))      # {"key": "u4f60u597d"}
    print(json.dumps(dic, ensure_ascii=False))  # {"key": "你好"}
    
    
    # json 的格式化输出
    data = {"username":["赵日天", "二愣子"], "gender":"male", "age":16}
    json_dic = json.dumps(data, sort_keys=True, indent=4, separators=(',',':'), ensure_ascii=False)
    print(json_dic)
    # 执行结果:
    # {
    #     "age":16,
    #     "gender":"male",
    #     "username":[
    #         "赵日天",
    #         "二愣子"
    #     ]
    # }


    五. configparser模块
    该模块适⽤于配置⽂件的格式与windows下的ini⽂件类似,可以包含⼀个或多个节(section),每个节
    可以有多个参数(键=值).

    import configparser
    config = configparser.ConfigParser()
    config['DEFAULT'] = {
     "sleep": 1000,
     "session_time_out": 30,
     "user_alive": 999999
    }
    config['TEST-DB'] = {
     "db_ip": "192.168.17.189",
     "port": "3306",
     "u_name": "root",
     "u_pwd": "123456"
    }
    config['168-DB'] = {
     "db_ip": "152.163.18.168",
     "port": "3306",
     "u_name": "root",
     "u_pwd": "123456"
    }
    config['173-DB'] = {
     "db_ip": "152.163.18.173",
     "port": "3306",
     "u_name": "root",
     "u_pwd": "123456"
    }
    f = open("db.ini", mode="w")
    config.write(f) # 写⼊⽂件
    f.flush()
    f.close()
    # 读取⽂件信息:
    config = configparser.ConfigParser()
    config.read("db.ini") # 读取⽂件
    print(config.sections()) # 获取到section章节, DEFAULT是给每个章节都配备的信息
    print(config.get("DEFAULT", "SESSION-TIME-OUT")) # 从xxx章节中读取到xxx信息
    
    # 也可以像字典⼀样操作
    print(config["TEST-DB"]['DB_IP'])
    print(config["173-DB"]["db_ip"])
    
    for k in config['168-DB']:
        print(k)
    
    for k, v in config["168-DB"].items():
        print(k, v)
    
    print(config.options('168-DB')) # 同for循环,找到'168-DB'下所有键
    print(config.items('168-DB')) # 找到'168-DB'下所有键值对
    print(config.get('168-DB','db_ip')) # 152.163.18.168   get⽅法Section下的key对应的value
    
    # 增删改操作:
    # 先读取,然后修改,最后写回⽂件
    config = configparser.ConfigParser()
    config.read("db.ini")   # 读取⽂件
    # 添加⼀个章节
    config.add_section("189-DB")
    config["189-DB"] = {
        "db_ip": "167.76.22.189",
        "port": "3306",
        "u_name": "root",
        "u_pwd": "123456"
    }
    # 修改信息
    config.set("168-DB", "db_ip", "10.10.10.168")
    # 删除章节
    config.remove_section("173-DB")
    # 删除元素信息
    config.remove_option("168-DB", "u_name")
    # 写回⽂件
    config.write(open("db.ini", mode="w"))
    
    
  • 相关阅读:
    [再寄小读者之数学篇](2014-07-17 一阶中值)
    对流体力学做出巨大贡献的杰出历史人物
    理科生毁灭世界
    [再寄小读者之数学篇](2014-07-17 行列式的计算)
    [再寄小读者之数学篇](2014-07-16 高阶导数的一个表达式)
    [再寄小读者之数学篇](2014-07-16 与对数有关的不等式)
    [再寄小读者之数学篇](2014-07-16 凹函数与次线性性)
    [再寄小读者之数学篇](2014-07-16 二阶中值)
    [再寄小读者之数学篇](2014-07-16 任意阶导数在零处为零的一个充分条件)
    对PostgreSQL xmin的深入学习
  • 原文地址:https://www.cnblogs.com/haitaoli/p/9822462.html
Copyright © 2020-2023  润新知