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 :指定静音状态。不提示您确认删除。
posted @ 2018-03-07 09:23  Clingyu  阅读(164)  评论(0)    收藏  举报