python序列化及加密算法的使用

一? 序列化模塊

什么叫序列化——將原本的字典、列表等內(nèi)容轉(zhuǎn)換成一個字符串的過程就叫做序列化。

比如,我們在python代碼中計算的一個數(shù)據(jù)需要給另外一段程序使用,那我們怎么給?

現(xiàn)在我們能想到的方法就是存在文件里,然后另一個python程序再從文件里讀出來。

但是我們都知道,對于文件來說是沒有字典這個概念的,所以我們只能將數(shù)據(jù)轉(zhuǎn)換成字典放到文件中。

你一定會問,將字典轉(zhuǎn)換成一個字符串很簡單,就是str(dic)就可以辦到了,為什么我們還要學(xué)習(xí)序列化模塊呢?

沒錯序列化的過程就是從dic 變成str(dic)的過程?,F(xiàn)在你可以通過str(dic),將一個名為dic的字典轉(zhuǎn)換成一個字符串,

但是你要怎么把一個字符串轉(zhuǎn)換成字典呢?

聰明的你肯定想到了eval(),如果我們將一個字符串類型的字典str_dic傳給eval,就會得到一個返回的字典類型了。

eval()函數(shù)十分強大,但是eval是做什么的?e官方demo解釋為:將字符串str當(dāng)成有效的表達(dá)式來求值并返回計算結(jié)果。

BUT!強大的函數(shù)有代價。安全性是其最大的缺點。

想象一下,如果我們從文件中讀出的不是一個數(shù)據(jù)結(jié)構(gòu),而是一句"刪除文件"類似的破壞性語句,那么后果實在不堪設(shè)設(shè)想。

而使用eval就要擔(dān)這個風(fēng)險。

所以,我們并不推薦用eval方法來進行反序列化操作(將str轉(zhuǎn)換成python中的數(shù)據(jù)結(jié)構(gòu))

序列化的目的

1、以某種存儲形式使自定義對象持久化;

2、將對象從一個地方傳遞到另一個地方。

3、使程序更具維護性。


Python可序列化的數(shù)據(jù)類型:

| 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)? #序列化:將一個字典轉(zhuǎn)換成一個字符串

print(type(str_dic),str_dic)? #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}

#注意,json轉(zhuǎn)換完的字符串類型的字典中的字符串是由""表示的

dic2 = json.loads(str_dic)? #反序列化:將一個字符串格式的字典轉(zhuǎn)換成一個字典

#注意,要用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) #也可以處理嵌套的數(shù)據(jù)類型

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方法接收一個文件句柄,直接將字典轉(zhuǎn)換成json字符串寫入文件

f.close()

f = open('json_file')

dic2 = json.load(f)? #load方法接收一個文件句柄,直接將文件中的json字符串轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)返回

f.close()

print(type(dic2),dic2)

其它參數(shù)說明:

Serialize obj to a JSON formatted str.(字符串表示的json對象)

Skipkeys:默認(rèn)值是False,如果dict的keys內(nèi)的數(shù)據(jù)不是python的基本類型(str,unicode,int,long,float,bool,None),設(shè)置為False時,就會報TypeError的錯誤。此時設(shè)置成True,則會跳過這類key

ensure_ascii:,當(dāng)它為True的時候,所有非ASCII碼字符顯示為\uXXXX序列,只需在dump時將ensure_ascii設(shè)置為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:應(yīng)該是一個非負(fù)的整型,如果是0就是頂格分行顯示,如果為空就是一行最緊湊顯示,否則會換行且按照indent的數(shù)值顯示前面的空白分行顯示,這樣打印出來的json數(shù)據(jù)也叫pretty-printed json

separators:分隔符,實際上是(item_separator, dict_separator)的一個元組,默認(rèn)的就是(‘,’,’:’);這表示dictionary內(nèi)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:將數(shù)據(jù)根據(jù)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數(shù)據(jù)類型間進行轉(zhuǎn)換

pickle,用于python特有的類型 和 python的數(shù)據(jù)類型間進行轉(zhuǎn)換

pickle模塊提供了四個功能:dumps、dump(序列化,存)、loads(反序列化,讀)、load ?(不僅可以序列化字典,列表...可以把python中任意的數(shù)據(jù)類型序列化

pickle

import pickle

dic = {'k1':'v1','k2':'v2','k3':'v3'}

str_dic = pickle.dumps(dic)

print(str_dic)? #一串二進制內(nèi)容

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如此強大,為什么還要學(xué)json呢?

這里我們要說明一下,json是一種所有的語言都可以識別的數(shù)據(jù)結(jié)構(gòu)。

如果我們將一個字典或者序列化成了一個json存在文件里,那么java代碼或者js代碼也可以拿來用。

但是如果我們用pickle進行序列化,其他語言就不能讀懂這是什么了~

所以,如果你序列化的內(nèi)容是列表或者字典,我們非常推薦你使用json模塊

但如果出于某種原因你不得不序列化其他的數(shù)據(jù)類型,而未來你還會用python對這個數(shù)據(jù)進行反序列化的話,那么就可以使用pickle

二? hashlib模塊

算法介紹

Python的hashlib提供了常見的摘要算法,如MD5,SHA1等等。

什么是摘要算法呢?摘要算法又稱哈希算法、散列算法。它通過一個函數(shù),把任意長度的數(shù)據(jù)轉(zhuǎn)換為一個長度固定的數(shù)據(jù)串(通常用16進制的字符串表示)。

摘要算法就是通過摘要函數(shù)f()對任意長度的數(shù)據(jù)data計算出固定長度的摘要digest,目的是為了發(fā)現(xiàn)原始數(shù)據(jù)是否被人篡改過。

摘要算法之所以能指出數(shù)據(jù)是否被篡改過,就是因為摘要函數(shù)是一個單向函數(shù),計算f(data)很容易,但通過digest反推data卻非常困難。而且,對原始數(shù)據(jù)做一個bit的修改,都會導(dǎo)致計算出的摘要完全不同。

我們以常見的摘要算法MD5為例,計算出一個字符串的MD5值:

import hashlib

md5 = hashlib.md5()

md5.update('how to use md5 in python hashlib?')

print md5.hexdigest()

計算結(jié)果如下:

d26a53750bc40b38b65a520292f69306

如果數(shù)據(jù)量很大,可以分塊多次調(diào)用update(),最后計算的結(jié)果是一樣的:

md5 = hashlib.md5()

md5.update('how to use md5 in ')

md5.update('python hashlib?')

print md5.hexdigest()

MD5是最常見的摘要算法,速度很快,生成結(jié)果是固定的128 bit字節(jié),通常用一個32位的16進制字符串表示。另一種常見的摘要算法是SHA1,調(diào)用SHA1和調(diào)用MD5完全類似:

import hashlib

sha1 = hashlib.sha1()

sha1.update('how to use sha1 in ')

sha1.update('python hashlib?')

print sha1.hexdigest()

SHA1的結(jié)果是160 bit字節(jié),通常用一個40位的16進制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不過越安全的算法越慢,而且摘要長度更長。

摘要算法應(yīng)用

任何允許用戶登錄的網(wǎng)站都會存儲用戶登錄的用戶名和口令。如何存儲用戶名和口令呢?方法是存到數(shù)據(jù)庫表中:

name | password

--------+----------

michael | 123456

bob? ? | abc999

alice? | alice2008

如果以明文保存用戶口令,如果數(shù)據(jù)庫泄露,所有用戶的口令就落入黑客的手里。此外,網(wǎng)站運維人員是可以訪問數(shù)據(jù)庫的,也就是能獲取到所有用戶的口令。正確的保存口令的方式是不存儲用戶的明文口令,而是存儲用戶口令的摘要,比如MD5:

username | password

---------+---------------------------------

michael? | e10adc3949ba59abbe56e057f20f883e

bob? ? ? | 878ef96e86145580c38c87f0410ad153

alice? ? | 99b1c2188db85afee403b1536010c2c9

考慮這么個情況,很多用戶喜歡用123456,888888,password這些簡單的口令,于是,黑客可以事先計算出這些常用口令的MD5值,得到一個反推表:

'e10adc3949ba59abbe56e057f20f883e': '123456'

'21218cca77804d2ba1922c33e0151105': '888888'

'5f4dcc3b5aa765d61d8327deb882cf99': 'password'

這樣,無需破解,只需要對比數(shù)據(jù)庫的MD5,黑客就獲得了使用常用口令的用戶賬號。

對于用戶來講,當(dāng)然不要使用過于簡單的口令。但是,我們能否在程序設(shè)計上對簡單口令加強保護呢?

由于常用口令的MD5值很容易被計算出來,所以,要確保存儲的用戶口令不是那些已經(jīng)被計算出來的常用口令的MD5,這一方法通過對原始口令加一個復(fù)雜字符串來實現(xiàn),俗稱“加鹽”:

hashlib.md5("salt".encode("utf8"))

經(jīng)過Salt處理的MD5口令,只要Salt不被黑客知道,即使用戶輸入簡單口令,也很難通過MD5反推明文口令。

但是如果有兩個用戶都使用了相同的簡單口令比如123456,在數(shù)據(jù)庫中,將存儲兩條相同的MD5值,這說明這兩個用戶的口令是一樣的。有沒有辦法讓使用相同口令的用戶存儲不同的MD5呢?

如果假定用戶無法修改登錄名,就可以通過把登錄名作為Salt的一部分來計算MD5,從而實現(xiàn)相同口令的用戶也存儲不同的MD5。

摘要算法在很多地方都有廣泛的應(yīng)用。要注意摘要算法不是加密算法,不能用于加密(因為無法通過摘要反推明文),只能用于防篡改,但是它的單向計算特性決定了可以在不存儲明文口令的情況下驗證用戶口令。

三? import的使用

import 模塊 先要怎么樣?

import tbjx 執(zhí)行一次tbjx這個模塊里面的所有代碼,

第一次引用tbjx這個模塊,會將這個模塊里面的所有代碼加載到內(nèi)存,只要你的程序沒有結(jié)束,接下來你在

引用多少次,它會先從內(nèi)存中尋找有沒有此模塊,如果已經(jīng)加載到內(nèi)存,就不再重復(fù)加載.

第一次導(dǎo)入模塊執(zhí)行三件事 ***

1. 在內(nèi)存中創(chuàng)建一個以tbjx命名的名稱空間.

2. 執(zhí)行此名稱空間所有的可執(zhí)行代碼(將tbjx.py文件中所有的變量與值的對應(yīng)關(guān)系加載到這

個名稱空間).

3. 通過tbjx. 的方式引用模塊里面的代碼.

import tbjx

print(tbjx.name)

tbjx.read1()

被導(dǎo)入模塊有獨立的名稱空間 ***

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)一接口,歸一化思想

導(dǎo)入多個模塊

import time, os, sys # 這樣寫不好

# 應(yīng)該向以下這種寫法:

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 容易與本文件的名字產(chǎn)生沖突.

# 1, 容易產(chǎn)生沖突,后者將前者覆蓋

# name = 'alex'

# from tbjx import name

# print(name)

3. 當(dāng)前位置直接使用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)

一行導(dǎo)入多個

from tbjx import name,read1,read2 # 這樣不好

from tbjx import name

from tbjx import read1

from ... import *

模塊循環(huán)導(dǎo)入的問題

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__)

# 當(dāng)tbjx.py做腳本: __name__ == __main__ 返回True

# 當(dāng)tbjx.py做模塊被別人引用時: __name__ == tbjx

# __name__ 根據(jù)文件的扮演的角色(腳本,模塊)不同而得到不同的結(jié)果

#1, 模塊需要調(diào)試時,加上 if __name__ == '__main__':

# import time

# change() # 測試代碼

# if __name__ == '__main__':

# change()

# 2, 作為項目的啟動文件需要用

模塊的搜索路徑

# import sm

import abc

# python 解釋器會自動將一些內(nèi)置內(nèi)容(內(nèi)置函數(shù),內(nèi)置模塊等等)加載到內(nèi)存中

import sys

# print(sys.modules) # 內(nèi)置內(nèi)容(內(nèi)置函數(shù),內(nèi)置模塊等等)

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' 路徑是當(dāng)前執(zhí)行文件的相對路徑

# import tbjx

# 我就想找到dz 內(nèi)存沒有,內(nèi)置中,這兩個你左右不了,sys.path你可以操作.

import sys

sys.path.append(r'D:\python_22\day16')

# sys.path 會自動將你的 當(dāng)前目錄的路徑加載到列表中.

import dz

# 如果你想要引用你自定義的模塊:

# 要不你就將這個模塊放到當(dāng)前目錄下面,要不你就手動添加到sys.path

import sm

1. 它會先從內(nèi)存中尋找有沒有已經(jīng)存在的以sm命名的名稱空間.

2. 它會從內(nèi)置的模塊中找. time,sys,os,等等.

3. 他從sys.path中尋找.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容