前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python3--序列化模块,hashlib模块

python3--序列化模块,hashlib模块

作者头像
py3study
发布2018-08-02 16:31:57
9110
发布2018-08-02 16:31:57
举报
文章被收录于专栏:python3

内置方法(回顾)

__len__    len(obj)的结果依赖于obj.__len__()的结果,计算对象的长度

__hash__   hash(obj)的结果依赖于obj.__hash__()的结果,计算对象的hash值

__eq__     obj1 == obj2的结果依赖于obj.__eq__()的结果,用来判断值相等

__str__    str(obj) print(obj) '%s'%obj 的结果依赖于__str__,用来做输出,显示

__repr__   repr(obj) '%r'%obj 的结果依赖于__repr__,还可以做str的备胎

__format__ format()的结果依赖于__format__的结果,是对象格式化

__call__   obj()相当于调用__call__,实现了__call__的对象是callable的

__new__    构造方法,在执行__init__之前执行,负责创建一个对象,在单例模式中有具体的应用

__del__    析构方法,在对象删除的时候,删除这个对象之前执行,主要用来关闭在对象中打开的系统资源

单例模式:只有一个对象,只开了一个内存空间

python中的单例模式,是依赖__new__来完成的

class A:

    def __getitem__(self, item):

        print(item)

a = A()

a['bbb']  # 对象['值']触发了__getitem__

#__getitem__   对象[]的形式对对象进行增删改查

# __setitem__

# __delitem__

# __delattr__   del obj.attr 用来自定义删除一个属性的方法

面试题

写一个类,定义100个对象

拥有三个属性name age sex

如果两个对象的name 和 sex 完全相同,就认为这是同一个对象,忽略age属性

做这100个对象的去重

需用用到的知识点

 hash算法 一个值 进行一系列的计算得出一个数字,让每一个不同的值计算出的数字都不相等

代码语言:javascript
复制
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __hash__(self):
        # hash算法本身就存在了 且直接在python中就能调用
        # 姓名相同 性别相同的对象的hash值应该相等才行
        return hash(self.name+self.sex)

    def __eq__(self, other):
        if self.name == other.name and self.sex == other.sex:return True

p_lst1 = []
for i in range(98):
    p_lst1.append(Person('egon' + str(i),i,'man'))
p_lst1.append(Person('egon50',200,'man'))
p_lst1.append(Person('egon51',300,'man'))
print(len(p_lst1))

# set对一个对象序列的去重,依赖于这个对象的两个方法: hash eq
print(len(set(p_lst1)))

执行结果

98

set对一个对象序列的去重,依赖于这个对象的两个方法: hash eq

key hash 数字 --> 内存地址 --> value

set hash 数字 --> 内存地址 --> set中的元素

如果值存在,则覆盖

blob.png
blob.png

set对一个对象序列的去重,如何判断这两个值是否相等

值a进行hash --> 存值

值b进行hash --> 判断值是否:相等 --> 说明是一样的

值b进行hash --> 判断值是否: 不相等  --> 在开辟一个空间,来存放b

python常用模块!!                         

什么叫序列化---将原本的字典,列表等内容转换成一个字符串的过程叫做序列化。

在python代码中计算的一个数据需要给另外一段程序使用,那我们怎么给?现在能想到的方法就是存在文件里,然后另一个python程序再从文件里读出来,很麻烦!

序列化的目的

1 以某种存储形式使自定义对象持久化

2 将对象从一个地方传递到另一个地方

3 使程序更具维护性

blob.png
blob.png

python中的序列化模块

json    所有的编程语言都通用的序列化格式,它支持的数据类型非常有限:数字,字符串,列表,字典

pickle  只能在python语言的程序之间传递数据用的,pickle支持python中所有的数据类型

shelve  python3.*之后才有的

序列化的使用场景:

1 能存储在文件中的一定是字符串 或者是 字节

2 能在网络上传输的 只有字节

json

json模块提供了四个功能:dumps,dump,loads,load

序列化:dumps 示例

代码语言:javascript
复制
import json
dic = {'张三': (175, 70, '开车')}
ret = json.dumps(dic)  # 序列化:将一个字典转换成一个字符串
print(type(dic), dic)  # 打印字典类型,内容
print(type(ret), ret)  # 打印序列化后的类型,内容
# 注意,json转换完的字符串类型的字典中的字符串是由""双引号表示

执行结果

反序列化:loads 示例

代码语言:javascript
复制
# 反序列化,变成原来的类型
import json
dic = {'张三': (175, 70, '开车')}
ret = json.dumps(dic)  # dumps序列化
res = json.loads(ret)  # loads反序列化
print(type(res), res)

执行结果

dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件

示例

代码语言:javascript
复制
import json
dic = {'张三': (175, 70, '开车')}
f = open('userinfo', 'w', encoding='utf-8')
# 先接收要序列化的对象,在接收文件句柄
json.dump(dic, f, ensure_ascii=False)  # 加上ensure_ascii=False,显示中文
f.close()

直接结果,打开文件看

blob.png
blob.png

load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回

代码语言:javascript
复制
import json
dic = {'张三': (175, 70, '开车')}
f = open('userinfo', 'r', encoding='utf-8')
ret = json.load(f)
print(ret)
f.close()

执行结果

{'张三': [175, 70, '开车']}

json个性化输出

代码语言:javascript
复制
import json
data = {'username':['李华','二愣子'],'sex':'male','age':16}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
# sort_keys 对每一个元素,做了排序
# indent 表示空白数,控制缩进
# separatots 分隔符
# ensure_ascii=False 不适用ascii编码显示
print(json_dic2)

执行结果

{

  "age":16,

  "sex":"male",

  "username":[

    "李华",

    "二愣子"

  ]

}

如果有多个字典,用json如何写入?

错误示例

代码语言:javascript
复制
import json
dic1 = {'张三': (175, 70, '开车')}
dic2 = {'李四': (170, 60, '旅游')}
dic3 = {'王五': (190, 80, '唱歌')}
f = open('user_info', 'a', encoding='utf-8')
json.dump(dic1, f, ensure_ascii=False)  # json.dump写进文件
json.dump(dic2, f, ensure_ascii=False)
json.dump(dic3, f, ensure_ascii=False)
f.close()
# json.load读取文件数据
f1 = open('user_info', 'r', encoding='utf-8')
print(json.load(f1))
f1.close()

执行报错,读取文件时报错

json.decoder.JSONDecodeError: Extra data: line 1 column 24 (char 23)

正确方法

代码语言:javascript
复制
import json
dic1 = {'张三': (175, 70, '开车')}
dic2 = {'李四': (170, 60, '旅游')}
dic3 = {'王五': (190, 80, '唱歌')}
f = open('user_info', 'a', encoding='utf-8')
st1 = json.dumps(dic1)  # 先用json.dumps序列化成字符串
f.write(st1 + '\n')     # 在write写入
st2 = json.dumps(dic2)
f.write(st2 + '\n')
st3 = json.dumps(dic3)
f.write(st3 + '\n')
f.close()
# 读取文件数据
f1 = open('user_info', 'r', encoding='utf-8')
for i in f1:
    print(json.loads(i.strip()))  # json.loads反序列化
f1.close()

执行结果

{'张三': [175, 70, '开车']}

{'李四': [170, 60, '旅游']}

{'王五': [190, 80, '唱歌']}

总结:

dumps序列化 loads反序列化 只在内存中操作数据 主要用于网络传输 和多个数据与文件打交道

dump序列化  load反序列化  主要用于一个数据直接存在文件里--直接和文件打交道

json不支持元组,不支持除了str数据类型之外的key

json在写入多次dump的时候,不能对应执行多次load来取出数据,pickle可以

json如果要写入多个元素 可以先将元素dumps序列化, f.write(序列化+'\n')写入文件,读出元素的时候,应该先按行读文件,在使用loads将读出来的字符串转换成对应的数据类型

pickle模块

pickle,用于python特有的类型和python的数据类型间进行转换(只能用于python程序之间的交互)

pickle模块提供了四个功能:dumps,dump(序列化,存),loads(反序列化,读),load(不仅可以序列化字典,列表...可以把python中任意的数据类型序列化)

示例

代码语言:javascript
复制
import pickle
dic = {(1999, 20, '开车'): "张三"}
ret = pickle.dumps(dic)  # 序列化结果 不是一个可读的字符串 而是一个bytes类型
print(ret)

执行结果

b'\x80\x03}q\x00M\xcf\x07K\x14X\x06\x00\x00\x00\xe5\xbc\x80\xe8\xbd\xa6q\x01\x87q\x02X\x06\x00\x00\x00\xe5\xbc\xa0\xe4\xb8\x89q\x03s.'

利用pickle.loads反序列化读

代码语言:javascript
复制
import pickle
dic = {(1999, 20, '开车'): "张三"}
ret = pickle.dumps(dic)  # 序列化结果 不是一个可读的字符串 而是一个bytes类型
print(pickle.loads(ret))  # 反序列化读

执行结果

{(1999, 20, '开车'): '张三'}

文件操作模式必须带b,因为它是bytes类型,所以不需要指定编码

写入pickle.dump

代码语言:javascript
复制
import pickle
dic = {(1999, 20, '开车'): "张三"}
f = open('test', 'wb')  # 因为是bytes类型,所以必须要带b,二进制
pickle.dump(dic, f)
f.close()

执行结果,像乱码一样的东西

�}q M�KX   开车q�qX   张三qs.

读取pickle.load,因为它是bytes类型,所以不需要指定编码

代码语言:javascript
复制
import pickle
dic = {(1999, 20, '开车'): "张三"}
f = open('test', 'rb')  # 因为是bytes类型,所以必须要带b,二进制
print(pickle.load(f))
f.close()

执行结果

{(1999, 20, '开车'): '张三'}

pickle多行操作

代码语言:javascript
复制
import pickle
# 关于写多行
# 写入文件
dic1 = {(1999, 21, '开车'): "张三"}
dic2 = {(1998, 22, '唱歌'): "李四"}
dic3 = {(1997, 23, '跳舞'): "王五"}
f = open('test1', 'wb')
pickle.dump(dic1, f)  # 序列化写入
pickle.dump(dic2, f)  # 序列化写入
pickle.dump(dic3, f)  # 序列化写入
f.close()
# 读取文件
f1 = open('test1', 'rb')
while True:  # 循环读取
    try:
        print(pickle.load(f1))
    except EOFError:
        break

执行结果

{(1999, 21, '开车'): '张三'}

{(1998, 22, '唱歌'): '李四'}

{(1997, 23, '跳舞'): '王五'}

关于序列化自定义类的对象

json错误示例

代码语言:javascript
复制
class A:
    pass
a = A()
import json
json.dumps(a)

执行报错

TypeError:

json只能是字符串

pickle示例

代码语言:javascript
复制
class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

a = A('Sam', 80)
import pickle
ret = pickle.dumps(a)  # 序列化一个对象
print(ret)
print(pickle.loads(ret))  # 读取对象(内存地址)
print(a.__dict__)

执行结果

b'\x80\x03c__main__\nA\nq\x00)\x81q\x01}q\x02(X\x03\x00\x00\x00ageq\x03KPX\x04\x00\x00\x00nameq\x04X\x03\x00\x00\x00Samq\x05ub.'

{'name': 'Sam', 'age': 80}

将序列化对象写入文件

代码语言:javascript
复制
class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

a = A('Sam', 80)
import pickle
ret = pickle.dumps(a)  # 序列化一个对象
f = open('classes', 'wb')
pickle.dump(a, f)  # 序列化对象a写入文件
f.close()
f1 = open('classes', 'rb')
obj = pickle.load(f1)  # 反序列化读取对象赋值给obj
print(obj.__dict__)

执行结果

{'age': 80, 'name': 'Sam'}

pickle错误示例

代码语言:javascript
复制
import pickle
f = open('classes', 'rb')
obj = pickle.load(f)
print(obj.__dict__)

执行报错

AttributeError: Can't get attribute 'A'

将对象反序列化时,必须保证该对象在内存中存在,否则读取报错

既然pickle如此强大,为什么还要学json呢?

这里要说明一下,json是一种所有的语言都可以识别的数据结构,而pickle只能是针对python程序而言

shelve序列化,在python3.X之后才新增的

shelve也是python提供给我们的序列化工具,比pickle用起来更简单一些

shelve只提供给我们一个open方法,是用key来访问的,使用起来和字典类似

示例

代码语言:javascript
复制
import shelve
f = shelve.open('shelve_file')
# 直接对文件句柄操作,就可以存入数据
f['key'] = {'int':10, 'float':9.5, 'string':'hello world'}
f.close()

执行结果,会产生3个文件

blob.png
blob.png

读取文件

代码语言:javascript
复制
import shelve
f = shelve.open('shelve_file')
# 直接对文件句柄操作,就可以存入数据
f['key'] = {'int':10, 'float':9.5, 'string':'hello world'}
f.close()
f1 = shelve.open('shelve_file')
s1 = f1['key']
print(s1)

执行结果

{'int': 10, 'string': 'hello world', 'float': 9.5}

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

代码语言:javascript
复制
import shelve
f = shelve.open('shelve_file', flag='r')  # flag = 'r' 表示只读方式
s1 = f['key']
f.close()
print(s1)

执行结果

{'int': 10, 'string': 'hello world', 'float': 9.5}

shelve中的一个bug

代码语言:javascript
复制
import shelve
f = shelve.open('shelve_file', flag='r')  # flag = 'r' 表示只读方式
#f['key']['int'] = 50  # 不能修改已有结构中的值
#f['key']['new'] = 'new'  # 不能在已有的结构中添加新的项
f['key'] = 'new'  # 只读却覆盖了字典 不是很明显这里是什么意思!
print(f['key'])  
f.close()

执行结果

new

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

新增一个属性

代码语言:javascript
复制
import shelve
f = shelve.open('shelve_file', writeback=True)
s1 = f['key']
f['key']['new_value'] = '人生苦短,我用Python'
print(s1)
f.close()

执行结果

{'int': 10, 'new_value': '人生苦短,我用Python', 'string': 'hello world', 'float': 9.5}

shelve的writeback方式有优点也有缺点,优点是减少了我们出错的概率,并且让对象的持久化对用户更加的透明了;使用writeback以后,shelf在open()的时候会增加额外的内存消耗

序列化的使用:推荐使用json和picker

hashlib模块

算法介绍

Python的hashlib提供了常见的摘要算法,如md5,sha1等等。

什么是摘要算法呢?摘要算法又称哈希算法,散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)

摘要算法就是通过摘要函数()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

用法:

1 文件的一致性校验

2 密文验证的时候加密

md5算法 通用的算法

sha算法 安全系数更高,sha算法有很多种,后面的数字越大安全系数越高,得到的数字结果越长,计算的时间越长

md5 加密示例

代码语言:javascript
复制
import hashlib
m = hashlib.md5()
m.update('Sam666'.encode('utf-8'))
print(m.hexdigest())

s = hashlib.md5()
s.update('Tom999'.encode('utf-8'))
print(s.hexdigest())

执行结果

1f7062b5dc99693c421c273838c36f48

bb740188c8025148b53505e12f559ddf

这个是16进制的数,固定长度,结果是唯一的(hex)表示16进制

md5不能反解

暴力破解和撞库

将所有常见的密码(123456,admin,111111之类的) 都进行md5摘要,生成一个md5密码库,对比查询(暴力破解和撞库的实现)

怎么解决呢?

加盐

代码语言:javascript
复制
# 加盐md5 123456
m = hashlib.md5('admin'.encode('utf-8'))
m.update('123456'.encode('utf-8'))
print(m.hexdigest())

# 没加盐md5 123456
m = hashlib.md5()
m.update('123456'.encode('utf-8'))
print(m.hexdigest())

执行结果

a66abb5684c45962d887564f08346e8d

e10adc3949ba59abbe56e057f20f883e

结果很明显不一样,如果加盐的密码不暴露,安全性要比单纯的md5高很多

更高级的方法(动态加盐)

代码语言:javascript
复制
import hashlib
username = input('输入账号:').strip()
password = input('输入密码:').strip()
m = hashlib.md5(username[::2].encode('utf-8'))  #根据用户名(字符串切片动态加盐)
m.update(password.encode('utf-8'))
print(m.hexdigest())

执行结果

输入账号:admin

输入密码:123456

a7aea2b468c1e775a411558475b661c7

因为每个用户名都是不同的,这种动态加盐的方式,会更安全,也不会被随便破解

文件的一致性校验

一段字符串直接进行摘要和分成几段摘要的结果是相同的

代码语言:javascript
复制
import hashlib
m = hashlib.md5()
m.update('hello,sam,I know your password is sam666'.encode('utf-8'))
print(m.hexdigest())

s = hashlib.md5()
s.update(b'hello,')  # 前面加b是转成bytes类型
s.update(b'sam,')
s.update(b'I know your ')
s.update(b'password is sam666')
print(s.hexdigest())

执行结果

d7a8902ae981eb2014d66a2e5f0a3574

d7a8902ae981eb2014d66a2e5f0a3574

文件内容的一致性校验

代码语言:javascript
复制
import hashlib
def check(filename):
    m = hashlib.md5()
    with open(filename, 'rb') as f:
        content = f.read()  # 直接读取所有内容
        m.update(content)   # 对内容进行摘要
    return m.hexdigest()
ret1 = check('file1')  # 执行前先创建两个文件,内容一样则md5码一样,有个标点不一样,md5则不同
ret2 = check('file2')
print(ret1)
print(ret2)

执行结果

7c31d05a8123bea369e06d400aee8f2f

7c31d05a8123bea369e06d400aee8f2f

上面那种方法不可取,如果文件很大,全部读取的话,内存不够,系统会卡

第二种方法,因为字符串分段后,格式内容一致,那么md5码也是一样的,所以可以每次读取部分内容,这样操作,结果也不会影响

代码语言:javascript
复制
import hashlib
def check(filename):
    m = hashlib.md5()
    with open(filename, 'rb') as f:
        while True:
            content = f.read(4096)  # 每次读取4096个字节
            if content:
                m.update(content)  # 对内容进行摘要
            else:
                break
    return m.hexdigest()
ret1 = check('file1')
ret2 = check('file2')
print(ret1)
print(ret2)

执行结果

7c31d05a8123bea369e06d400aee8f2f

7c31d05a8123bea369e06d400aee8f2f

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/04/23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档