1. 程式人生 > >python3__標準庫__序列化json / packle / shelve 模組

python3__標準庫__序列化json / packle / shelve 模組

1、序列化

1. 定義:把變數從記憶體中變成可儲存或可傳輸的過程稱之為序列化。在Python中叫pickling,在其他語言中也被稱之為serialization,marshalling,flattening等等,都是一個意思。

2. 序列化之後,就可以把序列化後的內容寫入磁碟,或者通過網路傳輸到別的機器上。

3. 把變數內容從序列化的物件重新讀到記憶體裡稱之為反序列化,即unpickling。

4. python提供了pickle模組來實現序列化。

5. 序列化目的:

(1)便於儲存。序列化過程將文字資訊轉變為二進位制資料流。這樣就資訊就容易儲存在硬碟之中,當需要讀取檔案的時候,從硬碟中讀取資料,然後再將其反序列化便可以得到原始的資料。防止斷電,記憶體中的資料丟失。

(2)便於傳輸。當兩個程序在進行遠端通訊時,彼此可以傳送各種型別的資料。無論是何種型別的資料,都會以二進位制序列的形式在網路上傳送。傳送方需要把這個物件轉換為位元組序列,在能在網路上傳輸;接收方則需要把位元組序列在恢復為物件。

2、pickle模組

(1)基本說明

1. Pickle的問題和所有其他程式語言特有的序列化問題一樣,就是pickle只能用於Python,並且可能不同版本的Python彼此都不相容,因此,只能用Pickle儲存那些不重要的資料,不能成功地反序列化也沒關係。

2. python3移除了cpickle模組

3. dumps()

dump()函式一樣執行的是序列化操作。不同的是dump()接收一個流物件並在檔案中寫入序列化後的資料,同時也接受待序列化的記憶體資料。

4. loads()load()函式一樣執行的是反序列化操作。不同的是load()函式接收一個流物件並去檔案讀取序列化後的資料,返回記憶體中的物件。

5. pickle模組是以二進位制的形式序列化後儲存到檔案中(儲存檔案的字尾為”.pkl”),不能直接開啟進行預覽。

# ! /usr/bin/env python
# coding:utf-8
# python interpreter:3.6.2
# author: admin_maxin
import pickle


obj = 123, "abcdedf", ["ac", 123], {"key": "value", "key1": "value1"}
print(type(obj), obj)                     # <class 'tuple'>
print("*" * 100)

# 1.序列化到檔案
with open("a.txt", "wb+") as f:
    pickle.dump(obj, f)

with open("a.txt", "rb+") as f:
    c = pickle.load(f)
    print("new type:", type(c), "\n", c)  # <class 'tuple'>

# 2.序列化到記憶體(bytes格式儲存),然後物件可以以任何方式處理如通過網路傳輸
obj1 = pickle.dumps(obj)
print(type(obj1), obj1)                   # <class 'bytes'>

c2 = pickle.loads(obj1)
print(type(c2), c2)                       # <class 'tuple'>

(2)序列化操作

pickle.dump(obj, file, protocol=None,*,fix_imports=True)

pickle.dumps(obj, protocol=None,*,fix_imports=True)

Pickler(file, protocol).dump(obj) == pickle.dump()

protocol引數說明:

        關於引數protocol,一共有5中不同的型別,即(0,1,2,3,4)。(0,1,2)對應的是python早期的版本,(3,4)則是在python3之後的版本。此外,引數可選 pickle.HIGHEST_PROTOCOL和pickle.DEFAULT_PROTOCOL。當前,python3.5版本中,pickle.HIGHEST_PROTOCOL的值為4,pickle.DEFAULT_PROTOCOL的值為3。當protocol引數為負數時,表示選擇的引數是pickle.HIGHEST_PROTOCOL。

(3)反序列化操作

pickle.load(file, *,fix_imports=True, encoding=”ASCII”. errors=”strict”)

# ----------  讀取的時候,引數protocol是自動選擇的,load()方法中沒有這個引數。

pickle.loads(bytes_object, *,fix_imports=True, encoding=”ASCII”. errors=”strict”)

pickle.Unpickler(file,*,fix_imports=True,encoding="ASCII",errors="strict") == pickle.load()

3、json模組

(1)基本說明

1. 如果我們要在不同的程式語言之間傳遞物件,就必須把物件序列化為標準格式,比如XML,但更好的方法是序列化為JSON,因為JSON表示出來就是一個字串,可以被所有語言讀取,也可以方便地儲存到磁碟或者通過網路傳輸。

2. JSON不僅是標準格式,並且比XML更快,而且可以直接在Web頁面中讀取,非常方便。 

3. JSON標準規定JSON編碼是UTF-8,所以我們總是能正確地在Python的str與JSON的字串之間轉換。

import json


# 1.序列化到記憶體
a = {"name": "maxin", "age": 23}
json_str = json.dumps(a)
print(type(json_str), json_str)                  # <class 'str'> {"name": "maxin", "age": 23}

target = json.loads(json_str)
print(type(target), target)                      # <class 'dict'> {'name': 'maxin', 'age': 23}
print("*" * 100)


# 2.序列化到檔案
a1 = {"name": "maxin", "age": 23}
with open("a1.txt", "w+") as f:
    json.dump(a1, f)

with open("a1.txt", "r+") as f:
    json_str1 = json.load(f)
    print(type(json_str1), json_str1)            # <class 'dict'> {'name': 'maxin', 'age': 23}

(2)json進階

        很多時候,我們更喜歡用class表示物件,比如定義Student類,然後序列化。但是此時我們會得到如下的錯誤提示:

提示類例項物件(s1)為不可序列化的json物件。

 

1. 因此可通過dump()或dumps()中的default__dict__引數進行類的序列化。

2. 因此可通過load()或loads()中的object_hook引數進行類的反序列化。

# -------------------------------------------------------------------------------------json進階
import json


class Student(object):

    def __init__(self, name="", age=0, score=0):
        self.name = name
        self.age = age
        self.score = score


if "__main__" == __name__:

    s1 = Student("maxin", 23, 97)
    # json_str = json.dumps(s1)
    # print(json_str)

    # 1.序列化
    # default(obj)是一個應返回obj的可序列化版本或引發TypeError的函式。 預設情況下只會引發TypeError。
    json_str = json.dumps(s1, default=lambda obj: obj.__dict__)

    # <class 'str'> {"name": "maxin", "age": 23, "socre": 97}
    print(type(json_str), json_str)


    # 2.反序列化
    def dict2student(d):
        return Student(d['name'], d['age'], d['score'])

    # 將使用``object_hook``的返回值而不是``dict``。 此功能可用於實現自定義解碼器(例如JSON-RPC類提示)。
    json_str_loads = json.loads(json_str, object_hook=dict2student)

    # <class '__main__.Student'> <__main__.Student object at 0x000001DACB081AC8>
    print(type(json_str_loads), json_str_loads)

4、python與json之間資料型別轉換對照列表

(1)Python 序列化為 JSON 型別轉換對應表

Python JSON
dict object
list, tuple array
str string
int, float, int- & float-derived Enums number
True true
False false
None null

(2)JSON 型別反序列化為python型別對應表

JSON Python
object dict
array list
string str
number (int) int
number (real) float
true True
false False
null None

5.shelve模組

shelve模組是一個簡單的將記憶體資料通過檔案持久化的模組,可以持久化任何pickle可支援的python資料格式。shelve物件是一個類字典的物件,可序列化多組資料,並通過key將不同的序列化資料分隔開,反序列化時就會非常的方便。另外,寫程式的時候如果不想用關係資料庫那麼重量級的去儲存資料,也可以用到shelve。需要注意:在shelve模組中key必須要是字串

(1)原始碼說明

shelve模組當中僅提供了一個函式那就是open()函式。該函式的主要作用是:開啟一個持久字典(persistent dictionary)方便後續進行讀(read)和寫(write)操作。

def open(filename, flag='c', protocol=None, writeback=False):
    """Open a persistent dictionary for reading and writing."""

    --filename: 檔名稱
    --flag: r(只讀)w(讀寫現存資料來源)c(讀寫新的或現存的資料來源)n(讀寫新的資料來源)
    --protocol: 可選的protocol引數指定pickle協議的版本(0,1或2)。
    --writeback: 自主選擇要不要做出修改後將拷貝寫會

    """
    The filename parameter is the base filename for the underlying
    database.  As a side-effect, an extension may be added to the
    filename and more than one file may be created.  The optional flag
    parameter has the same interpretation as the flag parameter of
    dbm.open(). The optional protocol parameter specifies the
    version of the pickle protocol (0, 1, or 2).

    See the module's __doc__ string for an overview of the interface.
    """

    return DbfilenameShelf(filename, flag, protocol, writeback)

(2)資料序列化與反序列化

(a)原始碼中註釋的部分說明:

To summarize the interface (key is a string, data is an arbitrary
object):
介面總結

        import shelve
        # 開啟檔案
        d = shelve.open(filename) # open, with (g)dbm filename -- no suffix
        # 按照鍵值對方式儲存資料。key存在則覆蓋值。
        d[key] = data   # store data at key (overwrites old data if
                        # using an existing key)
        # data儲存的是鍵對應的值的一個拷貝
        data = d[key]   # retrieve a COPY of the data at key (raise
                        # KeyError if no such key) -- NOTE that this
                        # access returns a *copy* of the entry!
        del d[key]      # delete data stored at key (raises KeyError
                        # if no such key)
        flag = key in d # true if the key exists
        # 獲取所有存在的鍵(效率非常的低下)
        list = d.keys() # a list of all existing keys (slow!)

        d.close()       # close it

(b)資料序列化

序列化會直接在當前python指令碼目錄下新建3個檔案。

filename.bak: 生成的資料檔案的備份檔案

filename.dat: 資料檔案(二進位制)

filename.dir: CPS備份檔案

import shelve


d = shelve.open("shelve_test")


class Test(object):
    def __init__(self, n=1):
        self.n = n


t = "this is the first"
t1 = [1, 2, 3]
t2 = (4, 5, 6)
t3 = set([7, 8, 9])
t4 = {"age": 24}
t5 = Test()
t5 = Test(11111)

try:
    d["t"] = t
    d["t1"] = t1
    d["t2"] = t2
    d["t3"] = t3
    d["t4"] = t4
    d["t5"] = t5
except KeyError as ke:
    exit("key error!")
finally:
    d.close()

(c)資料反序列化

此處強調一下:data = r["t4"],data引用的實際上是key(t4)對應資料的一份拷貝open()中引數writeback則可讓你自主選擇要不要做出修改後將拷貝寫回。

import shelve


r = shelve.open("shelve_test")
print(r["t4"])
r.close()

(3)shelve模組的注意事項

(a)shelve模組不允許多個應用同時對一個DB(檔案)進行寫操作

import shelve


# python3.6中將檔案處理模式設定成“只讀”並不起作用
db = shelve.open("shelve_test", flag="r")

print(db.get("t1"))
print(db.get("t2"))
db["t"] = "this is update"
print(db.get("t"))
db.close()

(b)open()函式中“writeback=True”引數說明

import shelve


list1 = ["ma", "xiao", "xing"]
db1 = shelve.open("test")
db1["lis"] = list1                   # ①通過“直接賦值”的方式寫入
db1["lis"].append("shui")            # ②
print(type(db1["lis"]), db1["lis"])  # ③<class 'list'> ['ma', 'xiao', 'xing']
db1.close()

# ------------------------------------------------------------------------------------------------------- #
# ②中key=lis對應的資料並沒有寫回
# 原因:當②中再次讀取db1["lis"]的時候,db1["lis"]返回的實際上是原始資料來源的一個copy,
#      對應的記憶體地址都不同。所以②中修改的內容並不會出現在下邊的③中的db1["lis"],因為其
#      是原始資料來源的一個全新的copy.
# ------------------------------------------------------------------------------------------------------- #

# ----------改進措施 1
import shelve

list2 = ["ma", "xiao", "xing"]
db2 = shelve.open('shelve_db2.dat')
db2["lis"] = list2
temp = db2["lis"]
temp.append("shui")
db2["lis"] = temp                      # 通過直接賦值,覆蓋掉原有的值
print(type(db2["lis"]), db2["lis"])    # <class 'list'> ['ma', 'xiao', 'xing', 'shui']
db2.close()


# ----------改進措施 2
import shelve

list3 = ["ma", "xiao", "xing"]
db2 = shelve.open("test", writeback=True)
db2["lis"] = list3
db2["lis"].append("shuai")
print(type(db2["lis"]), db2["lis"])      # <class 'list'> ['ma', 'xiao', 'xing', 'shuai']
db2.close()

(c)字典鍵值對無法寫回問題

import shelve


db3 = shelve.open("test")
dic = {"name": "maxiaoxing", "sex": "man"}
db3["dic"] = dic
db3["dic"]["age"] = 23

for k, v in db3.items():
    print(k, v)

db3.close()
# ------------------------------------------------------------------------------------------------------- #
# 列印結果顯示:對字典內容的新增操作並沒有寫回,列印操作僅僅顯示的是從原始資料的一個拷貝,
# 並不會顯示增加的鍵值對,若將open()函式的“writeback=”設定為True則會寫回成功。
# ------------------------------------------------------------------------------------------------------- #

(d)open()函式中writeback引數的優缺點

優點:①減少了出錯的概率,且讓物件的持久化對使用者更加的透明

缺點:①writeback=True之後,會將從DB中讀取的物件放入一個記憶體快取中,耗記憶體

           ②當close()之後記憶體快取中的物件會被重新寫入DB,耗時

(4)shelve模組open()函式writeback引數的應用例項

模擬儲存使用者的登入狀態:

使用者距離上一次登入時間不超過設定的“最大登入間隔時間”則可以繼續登入;

超過時間則無法使用原來的使用者名稱和密碼,需要重新進行註冊。

程式碼來源:https://blog.csdn.net/u012145252/article/details/80028146?utm_source=copy