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

内置方法(回顾)

__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算法 一个值 进行一系列的计算得出一个数字,让每一个不同的值计算出的数字都不相等

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中的元素

如果值存在,则覆盖

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

值a进行hash --> 存值

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

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

python常用模块!!                         

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

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

序列化的目的

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

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

3 使程序更具维护性

python中的序列化模块

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

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

shelve  python3.*之后才有的

序列化的使用场景:

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

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

json

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

序列化:dumps 示例

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

执行结果

反序列化:loads 示例

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

执行结果

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

示例

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

直接结果,打开文件看

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

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

执行结果

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

json个性化输出

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如何写入?

错误示例

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)

正确方法

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中任意的数据类型序列化)

示例

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反序列化读

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

执行结果

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

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

写入pickle.dump

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类型,所以不需要指定编码

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

执行结果

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

pickle多行操作

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错误示例

class A:
    pass
a = A()
import json
json.dumps(a)

执行报错

TypeError:

json只能是字符串

pickle示例

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}

将序列化对象写入文件

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错误示例

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来访问的,使用起来和字典类似

示例

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

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

读取文件

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

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

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()时候需要修改默认参数,否则对象的修改不会保存

新增一个属性

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 加密示例

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密码库,对比查询(暴力破解和撞库的实现)

怎么解决呢?

加盐

# 加盐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高很多

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

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

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

文件的一致性校验

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

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

文件内容的一致性校验

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码也是一样的,所以可以每次读取部分内容,这样操作,结果也不会影响

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏点滴积累

Scala 中方法扩展实践

这个名字不知道取得是否合适,简单来说要干的事情就是给某个类型添加一些扩展方法,此场景在各种语言中都会用到,比如 C# 语言,如果我们使用一个别人写好的类库,而又...

721
来自专栏智能算法

Python学习(二)---- 字符串操作、列表字典及深浅拷贝等

https://blog.csdn.net/fgf00/article/details/52061971

1092
来自专栏JavaQ

多参数方法进阶

很多高级工程师还在写包含N个参数的方法、使用setter方法构造实例,其实这些方式都是过时并且有很大缺陷的,本篇将深入讲解这些问题及解决方法。 多参数方法的问题...

34011
来自专栏互联网技术栈

《Effective Java 》系列一

编写实例受控类有几个原因。实例受控使得类可以确保他是一个Singleton或者是不可实例化的。他还使得不可变类可以确保不会存在两个相等的实例。

1804
来自专栏Python爬虫与算法进阶

PEP8规则及Pycharm应用

PEP8 PEP是 Python Enhancement Proposal 的缩写,翻译过来就是 Python增强建议书 PEP8 是什么呢,简单说就是一种编码...

4065
来自专栏老九学堂

【超详细】Java入门学习进阶知识点汇总

入门阶段,主要是培养Java语言的编程思想。了解Java语言的语法,书写规范等,掌握Eclipse、MyEclipse等开发工具,编写Java代码的能力。学完这...

4045
来自专栏章鱼的慢慢技术路

字符串练习——输入关键字找歌曲

1665
来自专栏Golang语言社区

《GO并发编程实战》—— 原子操作

我们已经知道,原子操作即是进行过程中不能被中断的操作。也就是说,针对某个值的原子操作在被进行的过程当中,CPU绝不会再去进行其它的针对该值的操作。无论这些其它的...

4417
来自专栏ASP.NETCore

.NET Core中妙用unsafe减少gc提升字符串处理性能

昨天在群里讨论怎么样效率的把一个字符串进行反转,一般的情况我们都知道,只要对String对象进行操作, 那么就会生成新的String对象,比如"1"+"2" 这...

4351
来自专栏博客园

MSIL学习------从HelloWorld开始

  前段时间突然想搞搞IL语言,于是在博客园中找到了包建强前辈关于IL的文章学习,并且在包前辈博客里看到了09年他与赵劼前辈关于是否有必要学习IL语言的争论,作...

1403

扫码关注云+社区

领取腾讯云代金券