Python基础知识——模块详解二
json和pickle模块
序列化:
把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。
序列化的作用:
1.持久保持状态
程序或者软件在执行的过程中会有一系列状态的变化,通常由各个语言所特有的数据结构保存在内存中
但是内存并不能长久存储数据,断电或者重启时,内存里的数据就会被清空,所以这些状态都应被存储在文件中
在断电或重启程序之前将程序当前内存中所有的数据都保存下来(保存到文件中),以便于下次程序运行时从文件中读取数据,然后接着执行操作,这就是序列化
2.跨平台的数据交流
序列化之后,不仅可以把序列化后的内容写入磁盘,还可以通过网络传输到别的机器上,
如果收发的双方约定好实用一种序列化的格式,那么便打破了平台/语言差异化带来的限制,实现了跨平台数据交互。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
json:

import json f = open('test.txt', 'w') dic = {'1':'a', '2': 'b'} f.write(json.dumps(dic)) f.close() f = open('test.txt', 'r') a = f.read() print(a) print(json.loads(a)) print(type(a), type(json.loads(a))) f.close() 运行结果: C:\Users\Clingyu\Desktop>python 1.py {"1": "a", "2": "b"} {'1': 'a', '2': 'b'} <class 'str'> <class 'dict'>
import json f = open('test.txt', 'w') dic = {'1':'a', '2': 'b', '3': 4} json.dump(dic, f) f.close() f = open('test.txt') a = json.load(f) print(a) print(type(a)) f.close() 运行结果: C:\Users\Clingyu\Desktop>python 1.py {'1': 'a', '2': 'b', '3': 4} <class 'dict'>
json实现了四个方法, load(), loads(), dumps(), dump()
dump, dumps是将内存中的结构数据装换为json标准字符串
load, loads是将json标准字符串转换为数据结构
没有s的方法比有s的方法多了对文件的操作
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下: 
注意:
import json #dct="{'1':111}"#json 不认单引号 #dct=str({"1":111})#报错,因为生成的数据还是单引号:{'one': 1} dct='{"1":"111"}' print(json.loads(dct)) 运行结果: C:\Users\Clingyu\Desktop>python 1.py {'1': '111'}
所以:
无论数据是怎样创建的,只要满足json格式,就可以json.loads出来,不一定非要dumps的数据才能loads
pickle:

import pickle f = open('test.txt', 'wb') dic = {'1':'a', '2': 'b'} f.write(pickle.dumps(dic)) f.close() f = open('test.txt', 'rb') a = f.read() print(a) print(pickle.loads(a)) print(type(a), type(pickle.loads(a))) f.close() 运行结果: C:\Users\Clingyu\Desktop>python 1.py b'\x80\x03}q\x00(X\x01\x00\x00\x001q\x01X\x01\x00\x00\x00aq\x02X\x01\x00\x00\x002q\x03X\x01\x00\x00\x00bq\x04u.' {'1': 'a', '2': 'b'} <class 'bytes'> <class 'dict'>
import pickle f = open('test.txt', 'wb') dic = {'1':'a', '2': 'b', '3': 4} pickle.dump(dic, f) f.close() f = open('test.txt', 'rb') a = pickle.load(f) print(a) print(type(a)) f.close() 运行结果: C:\Users\Clingyu\Desktop>python 1.py {'1': 'a', '2': 'b', '3': 4} <class 'dict'>
需要注意的是,pickle dump进文件的数据是字节型的,不能从文件中看懂,读取时也需要按字节读取
另外:
Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据
shelve模块
shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写;key必须为字符串,而值可以是python所支持的数据类型。
>>> import shelve >>> f = shelve.open(r'sheve.txt') >>> f['a'] = {'1' : 'aaa', '2' : 'bbb'} >>> f['123'] = {'ccc' : '333'} >>> f['try'] = '111' >>> f['try'] '111' >>> f['try'] = '222' >>> f['try'] '222' >>> f['a']['2'] 'bbb' >>> f.close()
xml模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json相似,但json使用起来更简单
在json诞生之前,xml为主流的数据交换协议,很多方面依然在使用
所以,该模块只要求能知道,不求掌握,关于序列化还是首选json
xml通过<>来标识数据结构:
xml文件示例: <?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协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:
import xml.etree.ElementTree as ET tree = ET.parse("xml.xml") root = tree.getroot() print(root.tag) #遍历xml文档 for child in root: print('========>',child.tag,child.attrib,child.attrib['name']) for i in child: print(i.tag,i.attrib,i.text) #只遍历year 节点 for node in root.iter('year'): print(node.tag,node.text) 运行结果: data ========> country {'name': 'Liechtenstein'} Liechtenstein rank {'updated': 'yes'} 2 year {} 2008 gdppc {} 141100 neighbor {'name': 'Austria', 'direction': 'E'} None neighbor {'name': 'Switzerland', 'direction': 'W'} None ========> country {'name': 'Singapore'} Singapore rank {'updated': 'yes'} 5 year {} 2011 gdppc {} 59900 neighbor {'name': 'Malaysia', 'direction': 'N'} None ========> country {'name': 'Panama'} Panama rank {'updated': 'yes'} 69 year {} 2011 gdppc {} 13600 neighbor {'name': 'Costa Rica', 'direction': 'W'} None neighbor {'name': 'Colombia', 'direction': 'E'} None year 2008 year 2011 year 2011
import xml.etree.ElementTree as ET tree = ET.parse("xml.xml") root = tree.getroot() #修改 for node in root.iter('year'): new_year=int(node.text)+1 node.text=str(new_year) node.set('updated','yes') node.set('version','1.0') tree.write('test.xml') #删除node for country in root.findall('country'): rank = int(country.find('rank').text) if rank > 50: root.remove(country) tree.write('output.xml')
自建xml文档: import xml.etree.ElementTree as ET new_xml = ET.Element("namelist") name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"}) age = ET.SubElement(name,"age",attrib={"checked":"no"}) sex = ET.SubElement(name,"sex") sex.text = '33' name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"}) age = ET.SubElement(name2,"age") age.text = '19' et = ET.ElementTree(new_xml) #生成文档对象 et.write("test.xml", encoding="utf-8",xml_declaration=True) ET.dump(new_xml) #打印生成的格式 运行结果: C:\Users\Clingyu\Desktop>python 1.py <namelist><name enrolled="yes"><age checked="no" /><sex>33</sex></name><name enrolled="no"><age>19</age></name></namelist>
hashlib模块
hash算法, 该模块主要提供 SHA1, SHA224, SHA256, SHA384, SHA512, MD5 算法
#MD5算法使用示例,其余算法用法完全一致:# >>> import hashlib >>> a = hashlib.md5() >>> a <md5 HASH object @ 0x02CA0B00> >>> a.update('hello'.encode('utf8')) >>> a.digest() b']A@*\xbcK*v\xb9q\x9d\x91\x10\x17\xc5\x92' >>> a.hexdigest() '5d41402abc4b2a76b9719d911017c592' >>> a.update(' world'.encode('utf8')) >>> a.digest() b'^\xb6;\xbb\xe0\x1e\xee\xd0\x93\xcb"\xbb\x8fZ\xcd\xc3' >>> a.hexdigest() '5eb63bbbe01eeed093cb22bb8f5acdc3' >>> b = hashlib.md5() >>> b.update('hello world'.encode('utf8')) #a, b生成的摘要完全一致,update仅增加加密字符串,不会覆盖,但生成的摘要长度不变 >>> b.digest() b'^\xb6;\xbb\xe0\x1e\xee\xd0\x93\xcb"\xbb\x8fZ\xcd\xc3' >>> b.hexdigest() '5eb63bbbe01eeed093cb22bb8f5acdc3' >>> c = a.copy() >>> c <md5 HASH object @ 0x02CA0B78> >>> a.block_size 64 >>> a.digest_size 16 >>> a.name 'md5'
三个特点:
- 1.内容相同则hash运算结果相同,内容稍微改变则hash值则变
- 2.不可逆推
- 3.相同算法:无论校验多长的数据,得到的哈希值长度固定。
以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密:
>>> a = hashlib.md5() >>> a.update('Hello'.encode('utf8')) >>> a.hexdigest() '8b1a9953c4611296a827abf8c47804d7' >>> b = hashlib.md5('hash'.encode('utf8')) >>> b.update('Hello'.encode('utf8')) >>> b.hexdigest() '932e50ba4109538fb979c4516cf4d3fc'
hmac模块
对创建时的key和内容进行进一步的处理然后再加密
>>> import hmac >>> a = hmac.new('hello'.encode('utf8')) >>> a <hmac.HMAC object at 0x02CEF7F0> >>> a.hexdigest() '20582f8e7d1d35a4461b7f9444e9f76d' >>> a.update('hello'.encode('utf8')) >>> a.hexdigest() 'a092156be8e7a5c59e88705f06c05904'
#要想保证hmac最终结果一致,必须保证: #1:hmac.new括号内指定的初始key一样 #2:无论update多少次,校验的内容累加到一起是一样的内容 import hmac h1=hmac.new(b'egon') h1.update(b'hello') h1.update(b'world') print(h1.hexdigest()) h2=hmac.new(b'egon') h2.update(b'helloworld') print(h2.hexdigest()) h3=hmac.new(b'egonhelloworld') print(h3.hexdigest()) 运行结果: C:\Users\Clingyu\Desktop>python 1.py f1bf38d054691688f89dcd34ac3c27f2 f1bf38d054691688f89dcd34ac3c27f2 bcca84edd9eeb86f30539922b28f3981
补充内容:
eval()函数
是Python的内置方法之一,用来执行一个字符串表达式,并返回表达式的值
语法:
eval(expression[, globals[, locals]])
参数:
expression -- 表达式
globals -- 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象
locals -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象
eval应用示例:
#Python计算器
>>> print(eval(input("请输入想要计算的表达式:")))
请输入想要计算的表达式:1*2+(3+6)
11
#注意与上式区别
>>> print(eval('input("请输入想要计算的表达式:")'))
请输入想要计算的表达式:1*2+(3+6)
1*2+(3+6)
作用域:
>>> test = {}
>>> test = {'a': 1, 'b': 2}
>>> eval('a + b', test)
3
用来将字符串中的数据类型转换为各种对应的数据类型:
>>> eval('[1, 2, 3]')
[1, 2, 3]
>>> eval('(1, 2, 3)')
(1, 2, 3)
>>> eval('{1, 2, 3}')
{1, 2, 3}
eval的危险性:
>>> eval(input())
print('hello, world')
hello, world
如果eval中的input接收的不是一般的字符串或者变量类型,而是命令
则解释器会直接将该指令执行,如果输入的是一些精心构造的恶意数据
则计算机会很危险
危险性就在于不对用户输入进行判断和处理,并且还无条件执行
恶意执行示例:
1、运行程序,如果用户恶意输入:
please input:__import__('os').system('dir')
则eval()之后,当前目录文件都会展现在用户前面。。。
2、运行程序,如果用户恶意输入:
please input:open('data.py').read()
如果,当前目录中恰好有一个文件,名为data.py,则恶意用户变读取到了文件中的内容。。。
3、运行程序,如果用户恶意输入:
please input:__import__('os').system('del delete.py /q')
如果,当前目录中恰好有一个文件,名为delete.py,则恶意用户删除了该文件。。。
/q :指定静音状态。不提示您确认删除。

浙公网安备 33010602011771号