我的Python成长之路--Day21--常用模块的功能介绍03(pickle、shelve、json、xml、configparser)

版权声明:此博客实属作者原创,转载请注明出处! https://blog.csdn.net/Onion_cy/article/details/83151553

今天继续介绍常用模块的使用:今天介绍到的序列化模块中,重点掌握JSON

什么是序列化:

我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化,也就是将内存中的数据结构转化为一张中间格式,并存到硬盘上.(其中中间格式是指:一种通用的数据形式,能够被多种编程语言识别,如可以被python识别,可以被Java识别,可以被c/c++识别等等,为了之后联网时间计算机之间的交互,必须保证这个数据可以跨平台使用,这时候中间格式就起到了很大的作用)

为什么要使用序列化

1.持久保存状态信息
一个软件或程序的执行就是在处理一系列的变化,在编程语言中,状态会以各种各样的数据类型(可简单理解为变量)被保存在内存中,但是内存的缺点又是无法永久保存数据,重启程序或者中间断电后内存中的数据就会被清空;在断电或者程序重启之前将程序中的数据提前保存下来(保存到文件中),以便于下次执行程序的时候能够从文件中加载之前的数据,然后继续执行,这个过程就是序列化
2.跨平台进行数据交互
序列化之后,不仅可以把序列化后的内容写入磁盘,还可以通过网络传输到其他的机器上,如果收发双方约定好使用一种序列化的格式,那么变打破了平台和语言的差异化带来的限制,实现了跨平台的数据交互.
和序列化相关的模块:重要程度和简单介绍

python中的三个用于序列化的模块:

pickle模块

pickle是一个python独有的用来序列化的模块,但是pickle存在的序列化问题和其他编程语言特有的序列化问题一样,就是它序列化后的数据只能用于python,并且不同版本的python彼此都不兼容,因此,只能用pickle保存那些不重要的数据.不能成功的反序列化也没有关系.
相对于pickle的缺点来说它也有优点,就是它可以识别python中所有的数据类型,因此可以用它来存储一些不重要的数据.
主要的功能:
pickle.dump        将数据进行序列化
pickle.load          将数据进行反序列化    
pickle.dumps      将数据进行序列化
pickle.loads        将数据进行反序列化
其中dumps和dump的区别,dump相当于是dumps的升级版,dump封装了对文件的写入功能,在使用的时候不需要再写将数据写入文件的命令,dump用的比较多一点,但是dumps也要会用
其中loads和load的区别,load相当于是loads的升级版,load封装了对文件的读取功能,在使用的时候不需要再写从文件中读取数据的命令,load用的比较多一点,但是loads也要会用

注意:pickle是将python中的数据类型转换为Bytes类型存储的,是以b模式存储的
应用举例:

import pickle
 
dic={'name':'alvin','age':23,'sex':'male'}
 
print(type(dic))      #<class 'dict'>
 
j=pickle.dumps(dic)
print(type(j))        #<class 'bytes'>
 
 
f=open('序列化对象_pickle','wb') #注意是w是写入str,wb是写入bytes,j是'bytes'
f.write(j)  #-------------------等价于pickle.dump(dic,f)
 
f.close()
#-------------------------反序列化
import pickle
f=open('序列化对象_pickle','rb')
 
data=pickle.loads(f.read())    #  等价于data=pickle.load(f)
 
 
print(data['age'])

shelve模块(xxx.shv)

shelve模块也是python独有的用于序列化的模块,进行了进一步的封装,它也和pickle存一样的问题,它的序列化也只能用于python
shelve和pickle的不同之处在于:shelve不用关心文件模式什么的,直接把它当做一个字典来看待,它可以直接对数据进行修改,而不用覆盖原来数据,意思就是shelve可以像字典一样指定修改某个值,即得到一个shelve对象之后,直接把它当成一个字典通过key进行操作.在这里要注意的是key必须是字符串,而值可以是python所支持的所有类型而pickle你想要进行修改只能用wb模式进行覆盖


shelve只有一个open函数def open(filename, flag='c', protocol=None, writeback=False):
open函数的参数:第一个需要指定一个文件名,第2,3个参数一般不做修改,最后一个writeback参数在需要使用shelve进行回写操作时,需要将它的值改为True
应用:

import shelve

f=shelve.open(r'sheve.txt')
 f['stu1_info']={'name':'egon','age':18,'hobby':['piao','smoking','drinking']}
 f['stu2_info']={'name':'gangdan','age':53}
 f['school_info']={'website':'http://www.pypy.org','city':'beijing'}

print(f['stu1_info']['hobby'])
f.close()

注意:不用管序列化后存出现了多个文件,只需要用.shv文件来进行操作就可以了

json模块(Java script object notation)(xxx.json)

如果我们想要在不同的编程语言之间传递对象,就必须吧对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便的村道磁盘或者通过网络传输传给其他机器.JSON不仅仅是标准格式,并且比XML更快,而且可以直接在Web界面中读取,非常方便.对于我们开发者而言,json就是一种通用的数据格式,任何语言都能解析.JSON表示的对象是标准的JavaScipt语言的对象
JSON和Python内置的数据类型对应如下:

JSON格式的语法规范:
最外层通常是一个字典或者列表{}or[],想要写一个JSON格式的数据,那么外层可以直接写{}
在JSON中,字符串必须是"abc"(双引号)
可以在刚开始创建的字典或列表中无限多的套用多个层次


Json的核心功能
json.dump    序列化
json.load      反序列化
json.dumps    序列化
json.loads      反序列化
json和pickle中的序列化和反序列的函数功能是一样的.

使用JSON时需要注意的点:

1.JSON不认单引号,在JSON中需要使用引号时必须要使用双引号

2.无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads

3.一个数据结构只dump一次,千万不要使用at模式一直往一个JSON数据中追加数据,那样会产生很大的麻烦,如果有需要,就新创一个文件往里边重新写入.

4.JSON是将python数据类型转为字符串格式存储的,存储时使用t模式读取和存储的

import json
dct="{'1':111}"      json 不认单引号
dct=str({"1":111})  报错,因为生成的数据还是单引号:{'one': 1}

dct='{"1":"111"}'
print(json.loads(dct))

conclusion:
        无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads

 注意点

Json功能示例:

import json
 
dic={'name':'alvin','age':23,'sex':'male'}
print(type(dic))                #<class 'dict'>
 
j=json.dumps(dic)
print(type(j))                  #<class 'str'>(这里pickle出来的是Bytes)
 
 
f=open('序列化对象','w')
f.write(j)  #-------------------等价于json.dump(dic,f)
f.close()
#-----------------------------反序列化<br>
import json
f=open('序列化对象')
data=json.loads(f.read())#  等价于data=json.load(f)

Json格式的文件在编写好之后最好在线解析一下查看一下有没有什么错误:

JSON文件示例:

{
    "pop":[
        {
            "name":"Dangerous",
            "author":"Michale Jackson",
            "year":1991
        }
    ],
    "classical":[
        {
            "name":" the Third Symphony",
            "author":"Beethoven",
            "year":1803
        }
    ],
    "rock":[
        {
            "name":"Nothing at all",
            "author":"CuiJian",
            "year":1986
        }
    ]
}

xml模块(可拓展的标记语言)(xxx.xml)


xml是实现不同语言或程序之间进行数据交换的协议,和json差不多,但是json使用起来更简单,不过在'古时候',在json还没诞生的黑暗年代,大家只能选择使用xml,至今很多传统公司如金融行业的很多系统的接口还主要是xml

xml学习的重点还是语法格式:
1.任何的起始标签都必须有一个结束标签 即一个<>对应一个</>
2.可以采用另一种简化语法,可以在一个标签中同时表示起始和结束标签,这种语法是在大于符号之前紧跟一个斜线</>,xml解析器会将其翻译成<百度百科词条></百度百科词条>.
3.标签必须按照合适顺序进行嵌套,所以结束标签必须按照镜像顺序匹配起始标签.这好比是将起始和结束标签看做是数学中的左右括号;在没有关闭所有的内部括号之前,是不能关闭外面的括号的.
4.所有的特性都必须有值
5.所有的特性都必须在值的两边加上双引号,注意:只能是双引号.其他的识别不了

一个标签的组成部分:
<tagname 属性名称="属性值">文本内容</tagname>
单标签的写法:<tagname 属性名称="属性值"/>

和json的区别:
相比较来说xml更加重量级(笨重),同样的数据,xml格式占用更多空间,最麻烦的地方是,xml的解析非常麻烦,你需要知道xml的数据里面到底是什么结构,xml更多的是编写(照着模板填空)解析的工作,通常框架都已经做好了
xml的经常使用的函数:

root.iter('year')      #全文搜索
root.find('country') #在root的子节点找,只找一个
root.findall('country') #在root的子节点找,找所有

import xml.etree.ElementTree as ElementTree
 解析d.xml
tree = ElementTree.parse("d.xml")
print(tree)
 获取根标签
rootTree = tree.getroot()


 三种获取标签的方式
第一种
 获取所有人的年龄 iter是用于在全文范围获取标签(在整棵树中查找,并且是找到所有子标签)
for item in rootTree.iter("age"):
      一个标签三个组成部分
     print(item.tag)    # 标签名称
     print(item.attrib) # 标签的属性
     print(item.text)   # 文本内容
第二种 从当前标签的子标签(当前节点的下个节点)中找到一个名称为age的标签  如果有多个 找到的是第一个
print(rootTree.find("age").attrib)
 第三种 从当前标签的子标签中找到所有名称为age的标签
print(rootTree.findall("age"))


 获取单个属性
stu = rootTree.find("stu")
print(stu.get("age"))
print(stu.get("name"))

 删除子标签
rootTree.remove(stu)


 添加子标签
 要先创建一个子标签
newTag = ElementTree.Element("这是新标签",{"一个属性":"值"})
rootTree.append(newTag)

 写入文件
tree.write("f.xml",encoding="utf-8")
<studentinfo>
      <stu age="20" name="张三">
         <girlfriend age="19" name="张三的女朋友" />
    </stu>
    <stu age="20" name="李四">
         <girlfriend age="19" name="李四的女朋友" />
    </stu>


<这是新标签 一个属性="值" /></studentinfo>

镜像关闭顺序例子:

    <a>
        <b>
            <c>
            </c>
        </b>
    </a>
xml中标签一定要按照镜像的顺序去开关标签:最先打开的最后关闭

一个xml数据文件的示例:和JSON一样XML数据在编写好之后也一定要在线解析一下,保证没有基本的错误

xml文件的数据和树形结构类似,<data></data>为树干,中间的<country></country>就相当于树干,所以xml模块中的函数也和tree有关

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

xml数据

configparser模块(用于解析配置信息的模块)(xxx.cfg)

配置信息通常是需要更改但是不需要经常更改的值
配置文件的内容:包含两个部分 分区:section选项:option 选项中的信息是:key=value的形式
配置文件示例:

 路径相关的配置
[path]
db_path = C://myfile/test.txt
 用户相关的配置
[user]
name = 高根
 服务相关的配置
[server]
url = 192.168.1.2

我们用的最多的就是get功能,用来从配置文件获取一个配置选项
configparser中的功能:
读取功能:
import configparser
config=configparser.ConfigParser()
config.read('a.cfg')    读取配置文件a.cfg
config.sections()        查看所有分区
config.options()        查看所有选项
config.iteams('sections1')    查看分区section1 下面所有的key=value的(key,value)格式
config.get('section1','user')            查看分区section1下user的值>>字符串格式
config.getint('section1','age')            查看分区section1下age的值>>整数格式
config.getboolean('section1','is_admin')查看分区section1下is_admin的值>>布尔值格式
config.getfloat('section1','salary')    查看分区section1下salary的值>>浮点型格式

改写功能:
import configparser        导入模块
config=configparser.ConfigParser()   创建一个解析器

config.read('a.cfg',encoding='utf-8')    读取配置文件信息
config.remove_section('section2')        删除分区section2
config.remove_section('section2','k1')    删除section2分区下的k1选项
config.has_section('section1')            判断是否存在section1分区
config.has_option('section1','')        判断是否存在有user
config.add_section('eric')                添加一个新分区eric
config.set('eric','name','eric)            在新分区eric下添加name=eric,eric=18的配置
config.set('eric','age',18)                这样写程序会报错,必须是字符串才可以
config.write(open('a.cfg','w'))            最后将修改的内容写入文件,完成最终的修改

示例:

做一个登录 首先查看配置文件 是否又包含 用户名和密码 如果有直接登录 如果没有就进行输入用户名密码登录
登录成功后 询问是否要保存密码  如果是 写入配置文件

import configparser
# 设置一个解析器
cf = configparser.ConfigParser()
cf.read("user.cfg",encoding="utf-8")  #读取文件

current_usr = None
# 如果有直接拿出登录成功
if cf.has_option("user","username"):
    print("%s登录成功!" % cf.get("user","username"))
    current_usr = {"username":cf.get("user","username"),
                   "pwd":cf.get("user","pwd")}
else:
    name = input("请输入用户名:")
    pwd = input("请输入密码:")
    if name == "admin" and pwd == "123":
        current_usr = {"username": name,
                       "pwd": pwd}
        print("登录成功")
        # 询问是否需要记住密码
        if input("需要记住密码吗? y/n") == "y":
            # 写入配置文件
            cf.add_section("user")
            cf.set("user","username",name)
            cf.set("user", "pwd", pwd)
            with open("user.cfg","wt",encoding="utf-8") as f:
                cf.write(f)

猜你喜欢

转载自blog.csdn.net/Onion_cy/article/details/83151553