一? 序列化模塊
什么叫序列化——將原本的字典绰筛、列表等內容轉換成一個字符串的過程就叫做序列化枢泰。
比如,我們在python代碼中計算的一個數據需要給另外一段程序使用铝噩,那我們怎么給衡蚂?
現在我們能想到的方法就是存在文件里,然后另一個python程序再從文件里讀出來骏庸。
但是我們都知道毛甲,對于文件來說是沒有字典這個概念的,所以我們只能將數據轉換成字典放到文件中具被。
你一定會問玻募,將字典轉換成一個字符串很簡單,就是str(dic)就可以辦到了一姿,為什么我們還要學習序列化模塊呢七咧?
沒錯序列化的過程就是從dic 變成str(dic)的過程。現在你可以通過str(dic)叮叹,將一個名為dic的字典轉換成一個字符串艾栋,
但是你要怎么把一個字符串轉換成字典呢?
聰明的你肯定想到了eval()蛉顽,如果我們將一個字符串類型的字典str_dic傳給eval蝗砾,就會得到一個返回的字典類型了。
eval()函數十分強大携冤,但是eval是做什么的悼粮?e官方demo解釋為:將字符串str當成有效的表達式來求值并返回計算結果。
BUT曾棕!強大的函數有代價矮锈。安全性是其最大的缺點。
想象一下睁蕾,如果我們從文件中讀出的不是一個數據結構,而是一句"刪除文件"類似的破壞性語句债朵,那么后果實在不堪設設想子眶。
而使用eval就要擔這個風險。
所以序芦,我們并不推薦用eval方法來進行反序列化操作(將str轉換成python中的數據結構)
序列化的目的
1臭杰、以某種存儲形式使自定義對象持久化;
2谚中、將對象從一個地方傳遞到另一個地方渴杆。
3寥枝、使程序更具維護性。
Python可序列化的數據類型:
| Python | JSON |
| dict? ? ? ? ? |? ? object? ? |
| list, tuple? |? ? array? ? |
| str? ? ? ? ? ? |? ? string? ? |
| int, float? ? |? ? number? |
| True? ? ? ? ? |? ? ? true? ? |
| False? ? ? ? ? |? ? ? false? ? |
| None? ? ? ? ? |? ? ? null? ? |
1.1 json模塊
Json模塊提供了四個功能:dumps磁奖、dump囊拜、loads、load
import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic)? #序列化:將一個字典轉換成一個字符串
print(type(str_dic),str_dic)? #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}
#注意比搭,json轉換完的字符串類型的字典中的字符串是由""表示的
dic2 = json.loads(str_dic)? #反序列化:將一個字符串格式的字典轉換成一個字典
#注意冠跷,要用json的loads功能處理的字符串類型的字典中的字符串必須由""表示
print(type(dic2),dic2)? #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic) #也可以處理嵌套的數據類型
print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
loads和dumps
dumps loads
import json
f = open('json_file','w')
dic = {'k1':'v1','k2':'v2','k3':'v3'}
json.dump(dic,f)? #dump方法接收一個文件句柄,直接將字典轉換成json字符串寫入文件
f.close()
f = open('json_file')
dic2 = json.load(f)? #load方法接收一個文件句柄身诺,直接將文件中的json字符串轉換成數據結構返回
f.close()
print(type(dic2),dic2)
其它參數說明:
Serialize obj to a JSON formatted str.(字符串表示的json對象)
Skipkeys:默認值是False蜜托,如果dict的keys內的數據不是python的基本類型(str,unicode,int,long,float,bool,None),設置為False時霉赡,就會報TypeError的錯誤橄务。此時設置成True,則會跳過這類key
ensure_ascii:穴亏,當它為True的時候蜂挪,所有非ASCII碼字符顯示為\uXXXX序列,只需在dump時將ensure_ascii設置為False即可迫肖,此時存入json的中文即可正常顯示锅劝。)
If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse).
If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity).
indent:應該是一個非負的整型,如果是0就是頂格分行顯示蟆湖,如果為空就是一行最緊湊顯示故爵,否則會換行且按照indent的數值顯示前面的空白分行顯示,這樣打印出來的json數據也叫pretty-printed json
separators:分隔符隅津,實際上是(item_separator, dict_separator)的一個元組诬垂,默認的就是(‘,’,’:’);這表示dictionary內keys之間用“,”隔開伦仍,而KEY和value之間用“:”隔開结窘。
default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError.
sort_keys:將數據根據keys的值進行排序。
To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.
import json
data = {'username':['李華','二愣子'],'sex':'male','age':16}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
print(json_dic2)
1.2 pickle模塊
用于序列化的兩個模塊
json充蓝,用于字符串 和 python數據類型間進行轉換
pickle隧枫,用于python特有的類型 和 python的數據類型間進行轉換
pickle模塊提供了四個功能:dumps、dump(序列化谓苟,存)官脓、loads(反序列化,讀)涝焙、load ?(不僅可以序列化字典卑笨,列表...可以把python中任意的數據類型序列化)
pickle
import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
print(str_dic)? #一串二進制內容
dic2 = pickle.loads(str_dic)
print(dic2)? ? #字典
import time
struct_time? = time.localtime(1000000000)
print(struct_time)
f = open('pickle_file','wb')
pickle.dump(struct_time,f)
f.close()
f = open('pickle_file','rb')
struct_time2 = pickle.load(f)
print(struct_time2.tm_year)
這時候機智的你又要說了,既然pickle如此強大仑撞,為什么還要學json呢赤兴?
這里我們要說明一下妖滔,json是一種所有的語言都可以識別的數據結構。
如果我們將一個字典或者序列化成了一個json存在文件里桶良,那么java代碼或者js代碼也可以拿來用座舍。
但是如果我們用pickle進行序列化,其他語言就不能讀懂這是什么了~
所以艺普,如果你序列化的內容是列表或者字典簸州,我們非常推薦你使用json模塊
但如果出于某種原因你不得不序列化其他的數據類型,而未來你還會用python對這個數據進行反序列化的話歧譬,那么就可以使用pickle
二? hashlib模塊
算法介紹
Python的hashlib提供了常見的摘要算法岸浑,如MD5,SHA1等等瑰步。
什么是摘要算法呢矢洲?摘要算法又稱哈希算法、散列算法缩焦。它通過一個函數读虏,把任意長度的數據轉換為一個長度固定的數據串(通常用16進制的字符串表示)。
摘要算法就是通過摘要函數f()對任意長度的數據data計算出固定長度的摘要digest袁滥,目的是為了發(fā)現原始數據是否被人篡改過盖桥。
摘要算法之所以能指出數據是否被篡改過,就是因為摘要函數是一個單向函數题翻,計算f(data)很容易揩徊,但通過digest反推data卻非常困難。而且嵌赠,對原始數據做一個bit的修改塑荒,都會導致計算出的摘要完全不同。
我們以常見的摘要算法MD5為例姜挺,計算出一個字符串的MD5值:
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?')
print md5.hexdigest()
計算結果如下:
d26a53750bc40b38b65a520292f69306
如果數據量很大齿税,可以分塊多次調用update(),最后計算的結果是一樣的:
md5 = hashlib.md5()
md5.update('how to use md5 in ')
md5.update('python hashlib?')
print md5.hexdigest()
MD5是最常見的摘要算法炊豪,速度很快凌箕,生成結果是固定的128 bit字節(jié),通常用一個32位的16進制字符串表示词渤。另一種常見的摘要算法是SHA1陌知,調用SHA1和調用MD5完全類似:
import hashlib
sha1 = hashlib.sha1()
sha1.update('how to use sha1 in ')
sha1.update('python hashlib?')
print sha1.hexdigest()
SHA1的結果是160 bit字節(jié),通常用一個40位的16進制字符串表示掖肋。比SHA1更安全的算法是SHA256和SHA512,不過越安全的算法越慢赏参,而且摘要長度更長志笼。
摘要算法應用
任何允許用戶登錄的網站都會存儲用戶登錄的用戶名和口令沿盅。如何存儲用戶名和口令呢?方法是存到數據庫表中:
name | password
--------+----------
michael | 123456
bob? ? | abc999
alice? | alice2008
如果以明文保存用戶口令纫溃,如果數據庫泄露腰涧,所有用戶的口令就落入黑客的手里。此外紊浩,網站運維人員是可以訪問數據庫的窖铡,也就是能獲取到所有用戶的口令。正確的保存口令的方式是不存儲用戶的明文口令坊谁,而是存儲用戶口令的摘要费彼,比如MD5:
username | password
---------+---------------------------------
michael? | e10adc3949ba59abbe56e057f20f883e
bob? ? ? | 878ef96e86145580c38c87f0410ad153
alice? ? | 99b1c2188db85afee403b1536010c2c9
考慮這么個情況,很多用戶喜歡用123456口芍,888888箍铲,password這些簡單的口令,于是鬓椭,黑客可以事先計算出這些常用口令的MD5值颠猴,得到一個反推表:
'e10adc3949ba59abbe56e057f20f883e': '123456'
'21218cca77804d2ba1922c33e0151105': '888888'
'5f4dcc3b5aa765d61d8327deb882cf99': 'password'
這樣,無需破解小染,只需要對比數據庫的MD5翘瓮,黑客就獲得了使用常用口令的用戶賬號。
對于用戶來講裤翩,當然不要使用過于簡單的口令资盅。但是,我們能否在程序設計上對簡單口令加強保護呢岛都?
由于常用口令的MD5值很容易被計算出來律姨,所以,要確保存儲的用戶口令不是那些已經被計算出來的常用口令的MD5臼疫,這一方法通過對原始口令加一個復雜字符串來實現择份,俗稱“加鹽”:
hashlib.md5("salt".encode("utf8"))
經過Salt處理的MD5口令,只要Salt不被黑客知道烫堤,即使用戶輸入簡單口令荣赶,也很難通過MD5反推明文口令。
但是如果有兩個用戶都使用了相同的簡單口令比如123456,在數據庫中,將存儲兩條相同的MD5值氮兵,這說明這兩個用戶的口令是一樣的把跨。有沒有辦法讓使用相同口令的用戶存儲不同的MD5呢?
如果假定用戶無法修改登錄名黎棠,就可以通過把登錄名作為Salt的一部分來計算MD5,從而實現相同口令的用戶也存儲不同的MD5哪廓。
摘要算法在很多地方都有廣泛的應用灭红。要注意摘要算法不是加密算法侣滩,不能用于加密(因為無法通過摘要反推明文),只能用于防篡改变擒,但是它的單向計算特性決定了可以在不存儲明文口令的情況下驗證用戶口令君珠。
三? import的使用
import 模塊 先要怎么樣?
import tbjx 執(zhí)行一次tbjx這個模塊里面的所有代碼,
第一次引用tbjx這個模塊,會將這個模塊里面的所有代碼加載到內存,只要你的程序沒有結束,接下來你在
引用多少次,它會先從內存中尋找有沒有此模塊,如果已經加載到內存,就不再重復加載.
第一次導入模塊執(zhí)行三件事 ***
1. 在內存中創(chuàng)建一個以tbjx命名的名稱空間.
2. 執(zhí)行此名稱空間所有的可執(zhí)行代碼(將tbjx.py文件中所有的變量與值的對應關系加載到這
個名稱空間).
3. 通過tbjx. 的方式引用模塊里面的代碼.
import tbjx
print(tbjx.name)
tbjx.read1()
被導入模塊有獨立的名稱空間 ***
import tbjx
# name = 'alex'
# print(name)
# print(tbjx.name)
#
# def read1():
# print(666)
# tbjx.read1()
#
# name = '日天'
# tbjx.change()
# print(name) # 日天
# print(tbjx.name) # barry
為模塊起別名 **
1 簡單,便捷.
# import hfjksdahdsafkd as sm
# print(sm.name)
2,有利于代碼的簡化.
# 原始寫法
# result = input('請輸入')
# if result == 'mysql':
# import mysql1
# mysql1.mysql()
# elif result == 'oracle':
# import oracle1
# oracle1.oracle()
# list.index()
# str.index()
# tuple.index()
# 起別名
# result = input('請輸入')
# if result == 'mysql':
# import mysql1 as sm
# elif result == 'oracle':
# import oracle1 as sm
# ''' 后面還有很多'''
# sm.db() # 統(tǒng)一接口,歸一化思想
導入多個模塊
import time, os, sys # 這樣寫不好
# 應該向以下這種寫法:
import time
import os
import sys
from ... import ...
from ... import ...的使用
# from tbjx import name
# from tbjx import read1
# from tbjx import read2
# print(name)
# print(globals())
# read1()
from ... import ... 與import對比 ***
1. from.. import 用起來更方便
from tbjx import name
print(name)
2. from...import 容易與本文件的名字產生沖突.
# 1, 容易產生沖突,后者將前者覆蓋
# name = 'alex'
# from tbjx import name
# print(name)
3. 當前位置直接使用read1和read2,執(zhí)行時,仍然以tbjx.py文件全局名稱空間 ***
# from tbjx import read1
#
# def read1():
# print(666)
# name = '大壯'
# read1()
# print(globals())
from tbjx import change
name = 'Alex'
print(name) # 'Alex'
change() # 'barry'
from tbjx import name
print(name)
一行導入多個
from tbjx import name,read1,read2 # 這樣不好
from tbjx import name
from tbjx import read1
from ... import *
模塊循環(huán)導入的問題
py文件的兩種功能py文件的兩個功能:
1. 自己用 腳本
2. 被別人引用 模塊使用
# print('from the tbjx.py')
__all__ = ['name', 'read1'] # 配合*使用
name = '太白金星'
def read1():
print('tbjx模塊:', name)
def read2():
print('tbjx模塊')
read1()
def change():
global name
name = 'barry'
print(name)
# print(__name__)
# 當tbjx.py做腳本: __name__ == __main__ 返回True
# 當tbjx.py做模塊被別人引用時: __name__ == tbjx
# __name__ 根據文件的扮演的角色(腳本,模塊)不同而得到不同的結果
#1, 模塊需要調試時,加上 if __name__ == '__main__':
# import time
# change() # 測試代碼
# if __name__ == '__main__':
# change()
# 2, 作為項目的啟動文件需要用
模塊的搜索路徑
# import sm
import abc
# python 解釋器會自動將一些內置內容(內置函數,內置模塊等等)加載到內存中
import sys
# print(sys.modules) # 內置內容(內置函數,內置模塊等等)
import time
# print(sys.path)
#['D:\\python_22\\day17', 'C:\\Python\\Python36\\python36.zip',
'C:\\Python\\Python36\\DLLs', 'C:\\Python\\Python36\\lib',
'C:\\Python\\Python36', 'C:\\Python\\Python36\\lib\\site-packages']# 'D:\\python_22\\day17' 路徑是當前執(zhí)行文件的相對路徑
# import tbjx
# 我就想找到dz 內存沒有,內置中,這兩個你左右不了,sys.path你可以操作.
import sys
sys.path.append(r'D:\python_22\day16')
# sys.path 會自動將你的 當前目錄的路徑加載到列表中.
import dz
# 如果你想要引用你自定義的模塊:
# 要不你就將這個模塊放到當前目錄下面,要不你就手動添加到sys.path
import sm
1. 它會先從內存中尋找有沒有已經存在的以sm命名的名稱空間.
2. 它會從內置的模塊中找. time,sys,os,等等.
3. 他從sys.path中尋找.