在各種python的專案中,我們時常要持久化的在系統中存盤各式各樣的python的資料結構,常用的比如字典等,尤其是在云服務型別中的python專案中,要持久化或者臨時的在快取中儲存一些用戶認證資訊和日志資訊等,最典型的比如在資料庫中存盤用戶的token資訊,在本文中我們將針對三種型別的python持久化存盤方案進行介紹,分別是json、pickle和python自帶的資料庫sqlite3,
使用json存盤字典物件
json格式的資料存盤也是云服務專案中常用的型別,具備十分輕量級和易使用的特性,這里我們展示一個案例:如何使用json格式存盤一個用python產生的斐波那契數列,斐波那契數列中的每一個元素,都等于前一個數和前前一個數的和,即:
,而最常見的斐波那契數列的前兩位數都是1,如下是一個產生斐波那契數列的python代碼:
import json
number = {1:1, 2:1}
for i in range(3, 11):
number[i] = number[i - 1] + number[i - 2]
print (number)
代碼的執行結果如下:
[dechin@dechin-manjaro store_class]$ python3 json_dic.py
{1: 1, 2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55}
我們可以從結果中看到,第10個斐波那契數是55,接下來我們來看看這樣的一個字典,如何持久化的存盤到json格式的檔案中,以下是一個使用的示例:
import json
number = {1:1, 2:1}
for i in range(3, 11):
number[i] = number[i - 1] + number[i - 2]
with open('number.json', 'w') as file:
json.dump(number, file)
with open('number.json', 'r') as file:
data = https://www.cnblogs.com/djdjdj123/p/json.load(file)
print (data)
執行這個python檔案,我們可以獲得如下所示的輸出:
[dechin@dechin-manjaro store_class]$ python3 json_dic.py
{'1': 1, '2': 1, '3': 2, '4': 3, '5': 5, '6': 8, '7': 13, '8': 21, '9': 34, '10': 55}
這里我們發現在當前目錄下產生了一個json的檔案:
[dechin@dechin-manjaro store_class]$ ll
總用量 8
-rw-r--r-- 1 dechin dechin 265 3月 20 12:32 json_dic.py
-rw-r--r-- 1 dechin dechin 85 3月 20 12:32 number.json
我們可以看一下這個json檔案中存盤了什么樣的資料:
[dechin@dechin-manjaro store_class]$ cat number.json
{"1": 1, "2": 1, "3": 2, "4": 3, "5": 5, "6": 8, "7": 13, "8": 21, "9": 34, "10": 55}
在驗證了相關的資料已經被持久化存盤了之后,同時我們也注意到一個問題,我們產生斐波那契數列的時候,索引1,2,3...
使用的是整型變數,但是存盤到json格式之后,變成了字串格式,我們可以使用如下的案例來說明這其中的區別:
import json
number = {1:1, 2:1}
for i in range(3, 11):
number[i] = number[i - 1] + number[i - 2]
with open('number.json', 'w') as file:
json.dump(number, file)
with open('number.json', 'r') as file:
data = https://www.cnblogs.com/djdjdj123/p/json.load(file)
print (data)
print (number[10])
print (data['10'])
print (data[10])
執行的輸出如下:
[dechin@dechin-manjaro store_class]$ python3 json_dic.py
{'1': 1, '2': 1, '3': 2, '4': 3, '5': 5, '6': 8, '7': 13, '8': 21, '9': 34, '10': 55}
55
55
Traceback (most recent call last):
File "json_dic.py", line 16, in <module>
print (data[10])
KeyError: 10
這里的輸出就有一個報錯資訊,這是因為我們使用了整型索引變數來尋找json存盤的字典物件中對應的值,但是因為前面存盤的時候這些整型的索引已經被轉換成了字串的索引,因此實際上在存盤的物件中已經不存在整型的鍵值,所以執行結果會報錯,而如果輸入的是字串型別的鍵值,則成功的找到了第10個斐波那契數,
使用pickle存盤字典物件
關于斐波那契數列的資訊,在上一章節中已經介紹,這里我們直接進入pickle的使用案例:
import pickle
number = {1:1, 2:1}
for i in range(3, 11):
number[i] = number[i - 1] + number[i - 2]
with open('number.pickle', 'wb') as file:
pickle.dump(number, file)
with open('number.pickle', 'rb') as file:
data = https://www.cnblogs.com/djdjdj123/p/pickle.load(file)
print (data)
這里注意一個細節,在json格式的存盤中我們使用的檔案打開格式是w,而在pickle這里我們使用的存盤檔案打開格式是wb,pickle的讀取也是用的rb的二進制的讀取格式,上述代碼的執行輸出結果如下:
[dechin@dechin-manjaro store_class]$ python3 pickle_dic.py
{1: 1, 2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55}
這里我們可以發現,由pickle所存盤的字典格式中的整型的索引也被成功的存盤起來,在當前目錄下產生了一個名為number.pickle的檔案就是持久化存盤的物件,
[dechin@dechin-manjaro store_class]$ ll
總用量 12
-rw-r--r-- 1 dechin dechin 320 3月 20 12:45 json_dic.py
-rw-r--r-- 1 dechin dechin 85 3月 20 12:46 number.json
-rw-r--r-- 1 dechin dechin 56 3月 20 12:44 number.pickle
-rw-r--r-- 1 dechin dechin 279 3月 20 12:44 pickle_dic.py
類似于json格式中的持久化讀取驗證,我們也可以簡單修改一個類似的pickle的案例:
import pickle
number = {1:1, 2:1}
for i in range(3, 11):
number[i] = number[i - 1] + number[i - 2]
with open('number.pickle', 'wb') as file:
pickle.dump(number, file)
with open('number.pickle', 'rb') as file:
data = https://www.cnblogs.com/djdjdj123/p/pickle.load(file)
print (data)
print (number[10])
print (data[10])
執行結果如下所示:
{1: 1, 2: 1, 3: 2, 4: 3, 5: 5, 6: 8, 7: 13, 8: 21, 9: 34, 10: 55}
55
55
從結果中我們發現存盤后的物件用一樣的讀取格式被成功讀取,
使用sqlite3存盤字典物件
在常用的Linux作業系統中都會自帶sqlite3資料庫,如果是windows和Mac的作業系統,可以按照這個教程中給的方案進行安裝,
SQLite是一個行程內的庫,實作了自給自足的、無服務器的、零配置的、事務性的 SQL 資料庫引擎,它是一個零配置的資料庫,這意味著與其他資料庫不一樣,您不需要在系統中配置,
就像其他資料庫,SQLite引擎不是一個獨立的行程,可以按應用程式需求進行靜態或動態連接,SQLite直接訪問其存盤檔案,

同時在python3的庫中一般也自帶了sqlite3,不需要自己安裝,下面我們用ipython演示一下如何在python中使用sqlite3資料庫:
[dechin@dechin-manjaro store_class]$ ipython
Python 3.8.5 (default, Sep 4 2020, 07:30:14)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import sqlite3
In [2]: conn = sqlite3.connect('test_sqlite3.db') # 如果有db檔案就讀取,沒有就創建
In [3]: cur = conn.cursor()
In [8]: sql_test_1 = '''CREATE TABLE number
...: (i NUMBER,
...: n NUMBER);''' # 創建一個名為number的表,有兩列資料i和n作為鍵值對
In [9]: cur.execute(sql_test_1) # 執行字串指令
Out[9]: <sqlite3.Cursor at 0x7f6fb14acc70>
In [10]: sql_test_2 = "INSERT INTO number VALUES(1,1)" # 插入新的資料
In [11]: cur.execute(sql_test_2)
Out[11]: <sqlite3.Cursor at 0x7f6fb14acc70>
In [12]: sql_test_2 = "INSERT INTO number VALUES(2,1)"
In [13]: sql_test_3 = "INSERT INTO number VALUES(2,1)"
In [14]: cur.execute(sql_test_3)
Out[14]: <sqlite3.Cursor at 0x7f6fb14acc70>
In [15]: sql_test_4 = "SELECT * FROM number WHERE i=1" # 檢索資料
In [16]: cur.execute(sql_test_4)
Out[16]: <sqlite3.Cursor at 0x7f6fb14acc70>
In [17]: cur.fetchall()
Out[17]: [(1, 1)]
In [18]: sql_test_5 = "SELECT * FROM number WHERE i>=1"
In [19]: cur.execute(sql_test_5)
Out[19]: <sqlite3.Cursor at 0x7f6fb14acc70>
In [20]: cur.fetchall() # 讀取檢索回傳值
Out[20]: [(1, 1), (2, 1)]
In [21]: for i in range(3, 11):
...: sql_test_6 = "SELECT * FROM number WHERE i={}".format(i-1)
...: cur.execute(sql_test_6)
...: select_result1 = cur.fetchall()[0][1]
...: sql_test_7 = "SELECT * FROM number WHERE i={}".format(i-2)
...: cur.execute(sql_test_7)
...: select_result2 = cur.fetchall()[0][1]
...: cur.execute("INSERT INTO number VALUES({},{})".format(i, select_result1+select_res
...: ult2))
...:
In [22]: sql_test_8 = "SELECT * FROM number WHERE i>=1"
In [23]: cur.execute(sql_test_8)
Out[23]: <sqlite3.Cursor at 0x7f6fb14acc70>
In [24]: cur.fetchall()
Out[24]:
[(1, 1),
(2, 1),
(3, 2),
(4, 3),
(5, 5),
(6, 8),
(7, 13),
(8, 21),
(9, 34),
(10, 55)]
In [25]: exit() # 退出ipython
在上述示例中我們演示了如何使用sqlite3創建資料庫和表,以及對表的內容的一些常用操作,在執行完上述示例后,會在當前目錄下產生一個新的db檔案:
[dechin@dechin-manjaro store_class]$ ll
總用量 24
-rw-r--r-- 1 dechin dechin 320 3月 20 12:45 json_dic.py
-rw-r--r-- 1 dechin dechin 85 3月 20 12:46 number.json
-rw-r--r-- 1 dechin dechin 56 3月 20 12:47 number.pickle
-rw-r--r-- 1 dechin dechin 315 3月 20 12:47 pickle_dic.py
-rw-r--r-- 1 dechin dechin 8192 3月 20 13:05 test_sqlite3.db
如果在運行程序中出現如下所示的報錯,就代表有其他的行程正在占用這個db檔案,因此會有行程將這個資料庫進行鎖定:
Traceback (most recent call last):
File "sqlite3_dic.py", line 15, in <module>
cur.execute("INSERT INTO number VALUES(1,1)")
sqlite3.OperationalError: database is locked
解決的辦法是,首先用fuser查看這個db檔案被哪個用戶所占用:
[dechin@dechin-manjaro store_class]$ fuser test_sqlite3.db
/home/dechin/projects/2021-python/store_class/test_sqlite3.db: 5120
我們查看到是5120這個行程占用了資料庫檔案,也是這個行程將資料庫鎖定了,通常這種情況出現的原因是,在python中執行的資料庫操作指令未成功完成,導致資料庫的行程沒有結束,而我們也無法再通過這個行程向資料庫中輸入新的指令,因此我們只能通過將該行程殺死的方案來解決這個問題:
[dechin@dechin-manjaro store_class]$ kill -9 5120
還有一個需要注意的點是,上面所用到的資料庫操作實際上并未真正的被保存到資料庫檔案中,需要經過commit之后才會被保存到資料庫檔案中,接下來我們還是用斐波那契數列的例子來演示資料庫操作的使用:
import sqlite3
from tqdm import trange
conn = sqlite3.connect('test_sqlite3.db')
cur = conn.cursor()
try:
sql_test_1 = '''CREATE TABLE number
(i NUMBER,
n NUMBER);'''
cur.execute(sql_test_1)
except:
pass
cur.execute("INSERT INTO number VALUES(1,1)")
cur.execute("INSERT INTO number VALUES(2,1)")
for i in trange(3, 11):
sql_test_6 = "SELECT * FROM number WHERE i={}".format(i - 1)
cur.execute(sql_test_6)
select_result1 = cur.fetchall()[0][1]
sql_test_7 = "SELECT * FROM number WHERE i={}".format(i - 2)
cur.execute(sql_test_7)
select_result2 = cur.fetchall()[0][1]
cur.execute("INSERT INTO number VALUES({},{})".format(i, select_result1 + select_result2))
cur.execute("SELECT * FROM number WHERE i=10")
print (cur.fetchall())
conn.commit()
cur.close()
conn.close()
在上述用例中我們補充了commit操作和close操作,一方面持久化的保存了資料,另一方面也避免因為程式中其他地方的問題而導致了前面所提到的資料庫被鎖定的問題,我們看一下這個用例的執行輸出情況:
[dechin@dechin-manjaro store_class]$ python3 sqlite3_dic.py
100%|█████████████████████████████████████████████████████████| 8/8 [00:00<00:00, 31775.03it/s]
[(10, 55)]
第10個斐波那契數被成功輸出,在資料庫的輸出中,使用的格式是一個串列包含多個元組的形式,其中每一個元組代表一個滿足檢索條件的鍵值對,每一個元組中的元素代表每一列的值,
前面提到了持久化保存的問題,我們也用一個簡單示例來驗證剛才經過commit之后是否被成功的保存起來了:
'''
學習中遇到問題沒人解答?小編創建了一個Python學習交流群:153708845
尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書!
'''
import sqlite3
from tqdm import trange
conn = sqlite3.connect('test_sqlite3.db')
cur = conn.cursor()
cur.execute("SELECT * FROM number WHERE i=10")
print (cur.fetchall())
conn.commit()
cur.close()
conn.close()
執行輸出如下:
[dechin@dechin-manjaro store_class]$ python3 test_recall_sqlite3.py
[(10, 55)]
這個結果表明前面存盤下來的斐波那契數列已經被持久化的保存到了資料庫檔案中,我們只要鏈接上該資料庫就可以隨時的讀取該資料,
總結
本文介紹了三種python的字典物件持久化存盤方案,包含json、pickle和資料庫sqlite,并且配合一個實際案例斐波那契數列來演示了不同解決方案的使用方法,這里三種方案實際上各有優劣,推薦的使用場景為:在輕量級、日常使用中可以重點使用json格式進行物件的存盤,我們也可以很方便的在系統上直接查看json格式的檔案內容;在多用戶或多行程使用的案例中,推薦使用pickle的方案,可以更高性能、更低開銷的持久化存盤python物件;如果是需要對外提供服務的,我們推薦可以直接使用sqlite,對外可以提供一個資料庫查詢的解決方案,便不需要在本地存盤大量的資料或者可以更方便的對大規模資料進行處理,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/495482.html
標籤:Python
