序列化模块

将原本的字典、列表转化成字符串的过程就叫序列化
序列化的目的

  • 以某种存储形式使自定义对象持久化
  • 将对象从一个地方传递到另一个地方
  • 使程序更具有维护性

![}UJIGB3A{FAA`8J_``1PRE.png

json模块

Json模块提供了四个功能:dumps、dump、loads、load

  1. import json
  2. dict1 = {"k1": "v1", "k2": "v2", "k3": "v3"}
  3. list1 = [1, ['a', 'b', 'c'], {"k2": "v2", "k3": "v3"}, (1, 2), True]
  4. str_dict1 = json.dumps(dict1)
  5. print("序列化字符串:", repr(str_dict1))
  6. dict2 = json.loads(str_dict1)
  7. print("反序列化字典:", repr(dict2))
  8. str_list1 = json.dumps(list1)
  9. print("序列化字符串:", repr(str_list1))
  10. list2 = json.loads(str_list1)
  11. print("反序列化列表:", repr(list2))
  • dumps | 参数 | 功能 | | —- | —- | | Skipkeys |
    1. 默认值为False
    1. 如果dict内的keys内数据不是python的基本类型, Skipkeys值为False时,会报TypeError的错误; Skipkeys值为True时,则会跳过这类key
    1. Skipkeys值为True时,所有的非ascii码字符会显示为\uXXXX序列,我们可以把ensure_ascii设置为False即可正常显示中文
    | | indent | 是一个非负的整型,如果值为0,则顶格分行显示;如果值为空,则会在这一行紧凑显示;如果值为其他,则会前面空白indent的值分行显示。
    这样打印出来的json数据也叫pretty-printed json | | ensure_ascii | 当值为True时,所有的非ascii码显示为\uXXXX序列,当值为False时,中文可以正常显示 | | separators | 分隔符,其实是一个(item_separators, dict_separators)的元组,默认是(‘,’, ‘:’),表示字典内keys之间用’,’隔开,而key和value之间由’:’隔开 | | sort_keys | 是否将数据根据keys的值进行排序 |
  1. import json
  2. data = {'name': '张三', 'sex': 'male', 'age': 18}
  3. json_dic = json.dumps(data, sort_keys=True, indent=2, separators=
  4. (',', ':'), ensure_ascii=False)
  5. print(json_dic)
  6. # 运行结果
  7. {
  8. "age":18,
  9. "name":"张三",
  10. "sex":"male"
  11. }

json.dump和json.load不常用,主要是针对文件操作进行序列化和反序列化

  1. import json
  2. v = {'k1': 'yh', 'k2': '小马过河'}
  3. f = open('xiaoma.txt', mode='w+', encoding='utf-8') # 文件不存在就会生成
  4. json.dump(v, f)
  5. f.close()
  6. # xiaoma.txt
  7. {"k1": "yh", "k2": "\u5c0f\u9a6c\u8fc7\u6cb3"}

pickle模块

pickle json
只能在python之间交互 可以在python与其它语言之间交互
可以序列化python所有数据类型 只能序列化python常规数据类型,如列表
pickle的序列化后的对象不可读 json序列化后的对象是可读的

pickle模块提供了四个功能:dumps、dump(序列化,存)、loads、load(反序列化,读)

简单来说,json模块和pickle模块都有 dumps、dump、loads、load四种方法,而且用法一样。不同的是json模块序列化出来的是通用格式,其它编程语言都认识,就是普通的字符串,而pickle模块序列化出来的只有python可以认识,其他编程语言不认识的,表现为乱码。

不过pickle可以序列化函数,但是其他文件想用该函数,在该文件中需要有该文件的定义

  1. import time
  2. struct_time = time.localtime(1000000000)
  3. print(struct_time)
  4. f = open('pickle_file','wb')
  5. pickle.dump(struct_time,f)
  6. f.close()
  7. f = open('pickle_file','rb')
  8. struct_time2 = pickle.load(f)
  9. print(struct_time2.tm_year)

shelve模块

shelve也是python提供的序列化工具,shelve只提供给我们一个open方法,是用key来访问的,使用起来和字典有些类似。
用途:shelve是对象持久化的保存方法,将对象保存到文件里,默认的数据存储文件是二进制的。

shelve使用方法:

  • 创建一个shelf对象,直接使用open函数即可
  • 如果想要再次访问这个shelf,只需要再次shelve.open()就可以了,然后我们可以像使用字典一样来使用这个shelf ```python import shelve f = shelve.open(‘shelve_file’) f[‘key’] = {‘int’:10,’str’:’hello’,’float’:0.123} f.close()

f1 = shelve.open(‘shelve_file’) ret = f1[‘key’] f1.close() print(ret)

  1. 这个模块有一个限制,就是不允许多个应用在同一时刻向同一个文件里进行写操作,所以如果我们知道应用只是进行读操作,可以把shelve以只读的方式打开:`f1 = shelve.open('shelve_file',flag='r')`
  2. shelve在默认情况下不会记录待持久化对象的任何修改,所以如果我们要保存对象的修改,需要在shelve.open时改默认参数writeback
  3. ```python
  4. import shelve
  5. f1 = shelve.open('shelve_file')
  6. print(f1['key'])
  7. f1['key']['k1'] = 'v1'
  8. f1.close()
  9. f2 = shelve.open('shelve_file',writeback=True)
  10. print(f2['key'])
  11. f2['key']['k1'] = 'hello'
  12. f2.close()

使用shelve实现一个简单的数据库。

  1. # 使用shelve实现一个简单数据库
  2. import shelve
  3. # 增加一个人的数据
  4. def store_person(db):
  5. pid = input("请输入一个id:")
  6. person = dict()
  7. person['name'] = input("请输入姓名:")
  8. person['age'] = input("请输入年龄:")
  9. person['phone'] = input("请输入电话号码:")
  10. # 将这条字典数据存入数据库文件中
  11. db[pid] = person
  12. print("存储信息:pid是%s,存储的内容是%s" % (pid, person))
  13. pass
  14. # 查找一个人的数据
  15. def search_person(db):
  16. pid = input("请输入要查询的id:")
  17. msg = input("请输入要查询的信息:(name、age、phone)")
  18. if pid in db.keys():
  19. value = db[pid][msg]
  20. print("pid %s 的 %s 是 %s" % (pid, msg, value))
  21. else:
  22. print("没找到输入的pid")
  23. pass
  24. # 修改一个人的数据
  25. def update_person(db):
  26. pid = input("请输入要修改的id:")
  27. msg = input("请输入要修改的信息:(name、age、phone)")
  28. newvalue = input("请输入新的内容:")
  29. if pid in db.keys():
  30. value = db[pid]
  31. value[msg] = newvalue
  32. print("pid %s 的 %s 修改为了%s" % (pid, msg, newvalue))
  33. else:
  34. print("没找到输入的pid")
  35. pass
  36. # 删除一个人的数据
  37. def del_person(db):
  38. pid = input("请输入要删除的id:")
  39. if pid in db.keys():
  40. del db[pid]
  41. print("pid %s的数据已被删除" % pid)
  42. else:
  43. print("没找到输入的pid")
  44. pass
  45. def print_all(db):
  46. print("打印所有数据:")
  47. for key, value in db.items():
  48. print(key, value)
  49. pass
  50. def enter_cmd():
  51. cmd = input("请输入命令:")
  52. cmd = cmd.strip().lower()
  53. return cmd
  54. def print_help():
  55. print("命令:")
  56. print("add 增加一个人的数据")
  57. print("del 删除一个人的数据")
  58. print("update 修改一个人的数据")
  59. print("search 查询一个人的数据")
  60. print("print_all 打印所有数据")
  61. print("quit 退出程序")
  62. print("help 查询帮助手册")
  63. pass
  64. def main():
  65. database = shelve.open('data_people', writeback=True)
  66. try:
  67. while True:
  68. cmd = enter_cmd()
  69. if cmd == 'add':
  70. store_person(database)
  71. elif cmd == 'del':
  72. del_person(database)
  73. elif cmd == 'update':
  74. update_person(database)
  75. elif cmd == 'search':
  76. search_person(database)
  77. elif cmd == 'print_all':
  78. print_all(database)
  79. elif cmd == 'help':
  80. print_help()
  81. elif cmd == 'quit':
  82. return
  83. finally:
  84. database.close()
  85. if __name__ == '__main__':
  86. main()
  87. # 运行结果
  88. 请输入命令:help
  89. 命令:
  90. add 增加一个人的数据
  91. del 删除一个人的数据
  92. update 修改一个人的数据
  93. search 查询一个人的数据
  94. print_all 打印所有数据
  95. quit 退出程序
  96. help 查询帮助手册
  97. 请输入命令:print_all
  98. 打印所有数据:
  99. 01 {'name': 'zhangsan', 'age': '18', 'phone': '101010101'}
  100. 请输入命令:add
  101. 请输入一个id02
  102. 请输入姓名:lisi
  103. 请输入年龄:20
  104. 请输入电话号码:2020202002
  105. 存储信息:pid02,存储的内容是{'name': 'lisi', 'age': '20', 'phone': '2020202002'}
  106. 请输入命令:del
  107. 请输入要删除的id02
  108. pid 02的数据已被删除
  109. 请输入命令:search
  110. 请输入要查询的id01
  111. 请输入要查询的信息:(nameagephonename
  112. pid 01 name zhangsan
  113. 请输入命令:update
  114. 请输入要修改的id01
  115. 请输入要修改的信息:(nameagephonephone
  116. 请输入新的内容:20202020
  117. pid 01 phone 修改为了20202020
  118. 请输入命令:print_all
  119. 打印所有数据:
  120. 01 {'name': 'zhangsan', 'age': '18', 'phone': '20202020'}
  121. 请输入命令:quit

hashlib模块

python的hashlib模块提供了常见的摘要算法,如MD5、SHA1等。

摘要算法就是通过一个摘要函数 f() 把任意长度的一条数据 data 计算为一个固定长度的数据 str(通常是十六进制字符串)。
摘要算法的目的是发现原来的数据是否被篡改过。原理是摘要函数是一个单向函数,由 f(data)算出 str 很容易,但是想由 str 反推 data 却十分困难。而且对原数据data哪怕做一bit的修改都会导致计算出来的摘要完全不同。

  1. import hashlib
  2. md5 = hashlib.md5()
  3. data = "how to use md5 in python hashlib?"
  4. md5.update(data.encode('utf-8'))
  5. print(md5.hexdigest())
  6. # 运行结果
  7. d26a53750bc40b38b65a520292f69306

如果数据量很大,可以分块多次调用update,最后计算的结果是一样的。

  1. import hashlib
  2. md5 = hashlib.md5()
  3. md5.update('how to use md5 '.encode('utf-8'))
  4. md5.update('in python hashlib?'.encode('utf-8'))
  5. print(md5.hexdigest())
  6. # 运行结果
  7. d26a53750bc40b38b65a520292f69306

MD5是最常见的摘要算法,生成结果是固定的128位(二进制),输出的通常是32位字符串(16进制)。另一种常见的摘要算法就是SHA1,它的调用与MD5完全类似。

  1. import hashlib
  2. sha1 = hashlib.sha1()
  3. sha1.update('how to use md5 '.encode('utf-8'))
  4. sha1.update('in python hashlib?'.encode('utf-8'))
  5. print(sha1.hexdigest())
  6. # 运行结果
  7. b752d34ce353e2916e943dc92501021c8f6bca8c

摘要算法应用

在允许用户登录的网站都会存储用户登录的用户名和密码等信息,通常是把数据存到数据库表中。

  1. name | password
  2. --------+----------
  3. michael | 123456
  4. bob | abc999
  5. alice | alice2008

如果用MD5来保护密码就是这样:

  1. username | password
  2. ---------+---------------------------------
  3. michael | e10adc3949ba59abbe56e057f20f883e
  4. bob | 878ef96e86145580c38c87f0410ad153
  5. alice | 99b1c2188db85afee403b1536010c2c9

然而有很多MD5碰撞工具,可以轻松将密码碰撞出来。

所以为了确保密码不会轻松暴露,通常会加入一个用户口令,即通过对原始数据加上一个复杂字符串,这个操作俗称“加盐”。我个人理解就像输入密码后,还要求输入一个验证码,密码加验证码一起进行MD5加密,这样即使通过MD5反推出明文,只要黑客不知道Sault也不知道密码是什么。

另外如果两个用户存储的密码都使用123456这样简单的口令,在数据库里就存了两条相同的MD5值,为了避免这个情况,也可以用加盐的方法来解决。比如假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

  1. # md5加密
  2. import hashlib
  3. user_name = "zhangsan"
  4. user_password = "123456"
  5. # 获取md5对象
  6. md5 = hashlib.md5()
  7. # 对用户名和密码进行md5加密
  8. md5.update((user_name + user_password).encode('utf-8'))
  9. salt_password = md5.hexdigest()
  10. print(salt_password)
  11. dict1 = {user_name, salt_password}
  12. login_name = input("请输入用户名:")
  13. login_password = input("请输入密码:")
  14. md5_login = hashlib.md5()
  15. # md5.update((login_name + login_password).encode('utf-8'))
  16. md5_login.update((login_name + login_password).encode('utf-8'))
  17. md5_pwd = md5.hexdigest()
  18. print(md5_pwd)
  19. if md5_pwd == salt_password:
  20. print("登录成功")
  21. else:
  22. print("登录失败")

需要注意的是,我一开始用md5 = hashlib.md5()获取了一个md5对象,然后用这个md5对象对数据库中存储的用户名user_name和密码user_password进行加密,接着我想继续用这个md5对象对下面用户输入的login_name和login_password进行加密,结果发现即使我输入正确,两次加密结果却不同。
可见参考链接:https://blog.csdn.net/YyangWwei/article/details/105100505

简单来说,就是每调用一次hashlib.md5()函数就要重新实例化一次对象,否则输入输出就不相匹配。原因是同一个md5对象如果多次调用hashlib.md5()函数,后一次调用是将历史输入与本次输入做拼接来运算,比如连续对“123”作两次md5运算,第二次的结果是与前一次输入拼接后的运算结果,即“123123”的运算结果。

  1. import hashlib
  2. md5 = hashlib.md5()
  3. md5.update('123'.encode('utf-8'))
  4. print(md5.hexdigest())
  5. md5.update('123'.encode('utf-8'))
  6. print(md5.hexdigest())
  7. md5_new = hashlib.md5()
  8. md5_new.update('123123'.encode('utf-8'))
  9. print(md5_new.hexdigest())
  10. # 运行结果
  11. 202cb962ac59075b964b07152d234b70
  12. 4297f44b13955235245b2497399d7a93
  13. 4297f44b13955235245b2497399d7a93