用戶:Xyy23330121/Python/json
在學習文本流(file object)之後,我們可以簡單地把運行時的數據存儲到文本文件中。在存儲時,選擇合適的格式可以讓存儲事半功倍。有多種格式可以用於存儲,比如:
上面的模塊及其支持的格式都是 Python 獨有的格式,且有時不夠安全。JSON 是一個起源自 JavaScript 的、通用的數據交換格式,且有內置的模塊為之提供支持。它易於閱讀和修改,相比上面的兩種格式而言,更適合作為我們存儲數據的方式。
備註:如果存儲的數據較為簡單,使用 csv 格式 有時更為方便。
Python 類型 | JSON 類型 | Python 類型 |
---|---|---|
dict | object | dict |
list | array | list |
tuple | ||
str | string | str |
int | number | int |
float | number | float |
True | true | True |
False | false | False |
None | null | None |
json 支持轉換的基本類型包括字符串、整數、浮點數、布爾值以及None。並支持以這些基本類型構成的字典、列表以及字典和/或列表的嵌套。
具體來講,在默認情況下,它會作出如右表所示的轉換。
讀者也可以通過設置轉換函數。在轉換 JSON 時,將不支持的類型轉換為支持的類型、並進行輸出。
備註:特別的,它還支持將「由 int 或 float 派生的枚舉」[3][4][5]轉換為[6]對應的「numbers」。由於此教程作者看不出「枚舉」有什麼作用(字典是完全的上位替代),本教程不會包含枚舉的內容。
json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
對於給出的、支持寫入的文本流 fp,將 obj 的內容轉換為 json 字符串,並用 fp.write() 寫入到文件中。
建議使用剛打開的、模式為 'w' 的文本流,以防文件已有內容或先前已經寫入過內容,導致寫入後的文件不符合 JSON 標準。對同一個文本流連續使用 json.dump 也會導致寫入後的文件不符合 JSON 標準。
json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
類似 json.dump
,但是轉換後的內容會被作為字符串返回。
skipkeys 默認為 False
。如果 skipkeys 為 True
,則在轉換字典時,跳過不支持的鍵。如果為 False
,則在遇到不支持的鍵時,會引發 TypeError。
ensure_ascii 默認為 True
。如果 ensure_ascii 為 True
,則會對所有非 ascii 字符進行轉義。否則,會原樣輸出字符。
check_circular 默認為 True
。如果為 True
它會檢查轉換時對容器類型的循環引用,如果有循環引用,則會報錯 ValueError: Circular reference detected
。如果為 False
,則不會進行檢查,並會由於循環引用引發 RecursionError (或者導致更糟的情況)。關於循環引用,參見下面的章節。
allow_nan 默認為 True
。如果為 True
,它會將嚴格的 JSON 規格外的浮點數 NaN 和無窮大轉換為其 JavaScript 等價物。如果為 False
,它會報錯 ValueError。
indent 用於設置輸出的縮進格式。
separators 用於設置輸出的分隔符。它是兩類分隔符的元組,默認為(', ', ': ')
。在使用時,比如,如果想要輸出最緊湊的版本,應當輸入刪去空格的(',', ':')
;而如果想要輸出不那麼緊湊的版本,則可以輸入形如(', ', ': ')
的。
default 應當輸入一個函數。這個函數應當能輸入一個不支持的對象(比如自定義類和自定義實例),並輸出一個支持轉換為JSON的對象(比如字典)。在轉換時,如果遇到某個對象無法被直接轉換為 JSON 格式,就會調用 default 來先轉換成支持的對象,再轉換成 JSON。
因此,default 函數應當輸入一個不被 JSON 支持的對象,並輸出一個可以被轉換為 JSON 格式的對象,或者報錯 TypeError。
sort_keys 默認為 False
。如果為 True
,它會把字典中鍵的順序排序後、再按排序進行輸出。
cls 參數指定編碼時使用的 JSON 編碼器對象的類型。默認為 json.JSONEncoder。
我們可以看以下示例:
import json
obj = {"one":1,"two":2,"all":["one","two"]}
print(json.dumps(obj, indent=None))
print(json.dumps(obj, indent=4))
輸出為:
{"one": 1, "two": 2, "all": ["one", "two"]}
{
"one": 1,
"two": 2,
"all": [
"one",
"two"
]
}
此處縮進設置為 4,即用 4 個空格進行縮進。顯然,設置了 indent 的,輸出的嵌套層次更清楚。而未設置 indent 的輸出則更緊湊、佔用更小的空間。
循環引用指的是比如以下的情況:
s = [0, 1]
s[1] = s
print(s[1][1][1][1][1][1][0]) #输出:0
print(s[1][1][1][1][1][1][1]) #输出:[0, [...]]
由於列表的實現方式,製造出這種循環是簡單而且可行的。Python 對於循環引用的處理很精妙——當檢測到循環時,就輸出省略號。
但是,json 格式不支持省略號。在轉化時,它會無盡地生成:
[0,[0,[0,[0,[0,[0,[0,...]]]]]]]
並導致錯誤。所以,在轉換成 json 時,一定要注意不要進行循環引用。
json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
對於給出的、支持讀取的文本流 fp,使用 fp.read() 讀取其內容,並返回對應的 Python 對象。
建議使用剛打開的、模式為 'r' 的文本流。
json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
類似 json.load
,但是輸入的並非文本流,而是 JSON 格式的字符串。
object_hook 應傳入一個函數。該函數輸入一個字典,返回一個任意對象。如果傳入此項,解碼時獲得的字典都會被傳入到該函數中,並被轉換為該函數返回的對象。
object_pairs_hook 與 object_hook 類似,但字典會先被轉換為列表,再傳入到該函數中。
如果這兩項都沒有指定,默認為返回解碼時獲得的原字典。
具體表現可以看以下示例:
import json
def p(obj):
print(obj, type(obj))
return "string"
j = json.loads(
json.dumps({"one":1,"two":2}),
object_hook = p)
print(j)
j = json.loads(
json.dumps({"one":1,"two":2}),
object_pairs_hook = p)
print(j)
輸出為:
{'one': 1, 'two': 2} <class 'dict'>
string
[('one', 1), ('two', 2)] <class 'list'>
string
parse_float 應指定為一個函數。該函數輸入 JSON 中表示浮點數的字符串,返回一個對象。我們可以簡單傳入 decimal.Decimal
使所有表示浮點數的 numbers 被轉換為 Decimal。
parse_int 與 parse_float 類似,但是輸入的是表示整數的字符串。
parse_constant 與上述兩者類似,但是輸入的是字符串 '-Infinity'、'Infinity' 或 'NaN'。
cls 參數指定解碼時使用的 JSON 解碼器對象的類型。默認為 json.JSONDecoder。
class json.JSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)
class json.JSONDecoder(*, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None)
創建編碼器和解碼器時,使用的參數與上面「基本使用」章節中,函數的參數是一致的。上面的函數先讀取參數 cls 的內容,確定要使用的編碼器 / 解碼器的類型,然後再用對應的類型構造器生成編碼器或解碼器的實例,並用實例的方法來進行轉換操作。
default(o) 輸入一個不被支持的對象,並引發 TypeError。如果在創建實例時,使用了 default 參數,則會用參數的內容覆蓋該方法。
encode(o) 輸入一個 Python 對象,輸出其對應的 json 字符串。
iterencode(o) 輸入一個 Python 對象,輸出一個由 encode(o) 的切片形成的迭代器,使得 "".join([chunk for chunk in json.JSONEncoder().iterencode(o)]) == json.JSONEncoder().encode(o)
。該方法用於將過長的字符串分多次寫到文件中,以節省內存開支。
decode(s) 傳入 json 字符串,返回對應的 Python 對象。
raw_decode(s) 傳入由 json 字符串開頭的字符串,返回一個元組。元組的內容如下所示:
j = json.dumps({1:2,3:4})
print(j, len(j)) #输出:{"1": 2, "3": 4} 16
dec = json.JSONDecoder()
r,i = dec.raw_decode(j+"s")
print(r,type(r),i,type(i)) #输出:{'1': 2, '3': 4} <class 'dict'> 16 <class 'int'>
返回的元組,第一個元素為傳入字符串中,json 的部分轉換的對象;第二個元素為傳入字符串中,其餘部分起始位置的索引。
讀者可以通過創建新的編碼器 / 解碼器類型,來自定義編碼器或解碼器,只要它實現了上面的所有方法和參數。
- ↑ https://docs.python.org/zh-cn/3.13/library/marshal.html#module-marshal
- ↑ https://docs.python.org/zh-cn/3.13/library/pickle.html#module-pickle
- ↑ 枚舉 - Enum https://docs.python.org/zh-cn/3.12/library/enum.html
- ↑ int派生的枚舉 - IntEnum https://docs.python.org/zh-cn/3.12/library/enum.html#enum.IntEnum
- ↑ int派生的枚舉 - IntFlag https://docs.python.org/zh-cn/3.12/library/enum.html#enum.IntFlag
- ↑ https://docs.python.org/zh-cn/3.12/library/json.html#json.JSONEncoder