全網最適合入門的物件導向程式設計教程:58 Python字串與序列化-序列化Web物件的定義與實現

FreakStudio發表於2024-11-07

全網最適合入門的物件導向程式設計教程:58 Python 字串與序列化-序列化 Web 物件的定義與實現

image

摘要:

如果我們要在不同的程式語言之間傳遞物件,就必須把物件序列化為標準格式,比如XML\YAML\JSON格式這種序列化Web物件。這種序列化Web物件容易與其他程式設計語言互動,可讀性強,容易被傳遞給其它系統或客戶端。

原文連結:

FreakStudio的部落格

往期推薦:

學嵌入式的你,還不會物件導向??!

全網最適合入門的物件導向程式設計教程:00 物件導向設計方法導論

全網最適合入門的物件導向程式設計教程:01 物件導向程式設計的基本概念

全網最適合入門的物件導向程式設計教程:02 類和物件的 Python 實現-使用 Python 建立類

全網最適合入門的物件導向程式設計教程:03 類和物件的 Python 實現-為自定義類新增屬性

全網最適合入門的物件導向程式設計教程:04 類和物件的Python實現-為自定義類新增方法

全網最適合入門的物件導向程式設計教程:05 類和物件的Python實現-PyCharm程式碼標籤

全網最適合入門的物件導向程式設計教程:06 類和物件的Python實現-自定義類的資料封裝

全網最適合入門的物件導向程式設計教程:07 類和物件的Python實現-型別註解

全網最適合入門的物件導向程式設計教程:08 類和物件的Python實現-@property裝飾器

全網最適合入門的物件導向程式設計教程:09 類和物件的Python實現-類之間的關係

全網最適合入門的物件導向程式設計教程:10 類和物件的Python實現-類的繼承和里氏替換原則

全網最適合入門的物件導向程式設計教程:11 類和物件的Python實現-子類呼叫父類方法

全網最適合入門的物件導向程式設計教程:12 類和物件的Python實現-Python使用logging模組輸出程式執行日誌

全網最適合入門的物件導向程式設計教程:13 類和物件的Python實現-視覺化閱讀程式碼神器Sourcetrail的安裝使用

全網最適合入門的物件導向程式設計教程:全網最適合入門的物件導向程式設計教程:14 類和物件的Python實現-類的靜態方法和類方法

全網最適合入門的物件導向程式設計教程:15 類和物件的 Python 實現-__slots__魔法方法

全網最適合入門的物件導向程式設計教程:16 類和物件的Python實現-多型、方法重寫與開閉原則

全網最適合入門的物件導向程式設計教程:17 類和物件的Python實現-鴨子型別與“file-like object“

全網最適合入門的物件導向程式設計教程:18 類和物件的Python實現-多重繼承與PyQtGraph串列埠資料繪製曲線圖

全網最適合入門的物件導向程式設計教程:19 類和物件的 Python 實現-使用 PyCharm 自動生成檔案註釋和函式註釋

全網最適合入門的物件導向程式設計教程:20 類和物件的Python實現-組合關係的實現與CSV檔案儲存

全網最適合入門的物件導向程式設計教程:21 類和物件的Python實現-多檔案的組織:模組module和包package

全網最適合入門的物件導向程式設計教程:22 類和物件的Python實現-異常和語法錯誤

全網最適合入門的物件導向程式設計教程:23 類和物件的Python實現-丟擲異常

全網最適合入門的物件導向程式設計教程:24 類和物件的Python實現-異常的捕獲與處理

全網最適合入門的物件導向程式設計教程:25 類和物件的Python實現-Python判斷輸入資料型別

全網最適合入門的物件導向程式設計教程:26 類和物件的Python實現-上下文管理器和with語句

全網最適合入門的物件導向程式設計教程:27 類和物件的Python實現-Python中異常層級與自定義異常類的實現

全網最適合入門的物件導向程式設計教程:28 類和物件的Python實現-Python程式設計原則、哲學和規範大彙總

全網最適合入門的物件導向程式設計教程:29 類和物件的Python實現-斷言與防禦性程式設計和help函式的使用

全網最適合入門的物件導向程式設計教程:30 Python的內建資料型別-object根類

全網最適合入門的物件導向程式設計教程:31 Python的內建資料型別-物件Object和型別Type

全網最適合入門的物件導向程式設計教程:32 Python的內建資料型別-類Class和例項Instance

全網最適合入門的物件導向程式設計教程:33 Python的內建資料型別-物件Object和型別Type的關係

全網最適合入門的物件導向程式設計教程:34 Python的內建資料型別-Python常用複合資料型別:元組和命名元組

全網最適合入門的物件導向程式設計教程:35 Python的內建資料型別-文件字串和__doc__屬性

全網最適合入門的物件導向程式設計教程:36 Python的內建資料型別-字典

全網最適合入門的物件導向程式設計教程:37 Python常用複合資料型別-列表和列表推導式

全網最適合入門的物件導向程式設計教程:38 Python常用複合資料型別-使用列表實現堆疊、佇列和雙端佇列

全網最適合入門的物件導向程式設計教程:39 Python常用複合資料型別-集合

全網最適合入門的物件導向程式設計教程:40 Python常用複合資料型別-列舉和enum模組的使用

全網最適合入門的物件導向程式設計教程:41 Python常用複合資料型別-佇列(FIFO、LIFO、優先順序佇列、雙端佇列和環形佇列)

全網最適合入門的物件導向程式設計教程:42 Python常用複合資料型別-collections容器資料型別

全網最適合入門的物件導向程式設計教程:43 Python常用複合資料型別-擴充套件內建資料型別

全網最適合入門的物件導向程式設計教程:44 Python內建函式與魔法方法-重寫內建型別的魔法方法

全網最適合入門的物件導向程式設計教程:45 Python實現常見資料結構-連結串列、樹、雜湊表、圖和堆

全網最適合入門的物件導向程式設計教程:46 Python函式方法與介面-函式與事件驅動框架

全網最適合入門的物件導向程式設計教程:47 Python函式方法與介面-回撥函式Callback

全網最適合入門的物件導向程式設計教程:48 Python函式方法與介面-位置引數、預設引數、可變引數和關鍵字引數

全網最適合入門的物件導向程式設計教程:49 Python函式方法與介面-函式與方法的區別和lamda匿名函式

全網最適合入門的物件導向程式設計教程:50 Python函式方法與介面-介面和抽象基類

全網最適合入門的物件導向程式設計教程:51 Python函式方法與介面-使用Zope實現介面

全網最適合入門的物件導向程式設計教程:52 Python函式方法與介面-Protocol協議與介面

全網最適合入門的物件導向程式設計教程:53 Python字串與序列化-字串與字元編碼

全網最適合入門的物件導向程式設計教程:54 Python字串與序列化-字串格式化與format方法

全網最適合入門的物件導向程式設計教程:55 Python字串與序列化-位元組序列型別和可變位元組字串

全網最適合入門的物件導向程式設計教程:56 Python字串與序列化-正規表示式和re模組應用

全網最適合入門的物件導向程式設計教程:57 Python字串與序列化-序列化與反序列化

更多精彩內容可看:

給你的 Python 加加速:一文速通 Python 平行計算

一文搞懂 CM3 微控制器除錯原理

肝了半個月,嵌入式技術棧大彙總出爐

電子計算機類比賽的“武林秘籍”

一個MicroPython的開源專案集錦:awesome-micropython,包含各個方面的Micropython工具庫

Avnet ZUBoard 1CG開發板—深度學習新選擇

SenseCraft 部署模型到Grove Vision AI V2影像處理模組

文件和程式碼獲取:

可訪問如下連結進行對文件下載:

https://github.com/leezisheng/Doc

image

本文件主要介紹如何使用 Python 進行物件導向程式設計,需要讀者對 Python 語法和微控制器開發具有基本瞭解。相比其他講解 Python 物件導向程式設計的部落格或書籍而言,本文件更加詳細、側重於嵌入式上位機應用,以上位機和下位機的常見串列埠資料收發、資料處理、動態圖繪製等為應用例項,同時使用 Sourcetrail 程式碼軟體對程式碼進行視覺化閱讀便於讀者理解。

相關示例程式碼獲取連結如下:https://github.com/leezisheng/Python-OOP-Demo

正文

序列化 Web 物件

如果我們要在不同的程式語言之間傳遞物件,就必須把物件序列化為標準格式,比如 XML\YAML\JSON 格式這種序列化 Web 物件。這種序列化 Web 物件容易與其他程式設計語言互動,可讀性強,容易被傳遞給其它系統或客戶端。

在 Python 最常用的序列化 Web 物件就是環境配置的 yaml 檔案,anaconda 可以管理不同的環境配置,當我們想將自己的環境配置分享給其他人時,就可以生成 yaml 檔案,這樣別人可以快速匯入 yaml 檔案構建和我們一樣的環境來執行程式碼。

我們可以在命令列中使用如下指令生成 yaml 檔案:

conda env export > environment.yml

image

使用 JSON 實現序列化 Web 物件

JavaScript Object Notation(JSON)是一種人類可讀的格式,用於儲存基礎資料型別。JSON 是一種標準格式,可以被各式各樣的客戶端系統解析。因此,JSON 非常適合用於在完全不同的系統之間進行資料傳輸。而且,JSON 不支援任何可執行程式碼,只有資料可以被序列化;因此,更難向其中植入惡意程式碼。JSON 格式因其易於被 JavaScript 引擎解析的特性,常被用於 Web 伺服器與具備 JavaScript 功能的瀏覽器間的資料傳輸。若 Web 應用的伺服器端採用 Python 編寫,為確保資料的相容性與通用性,需將內部資料轉換為 JSON 格式。

我們可以使用 Python 中的 json 模組生成 JSON 檔案,該模組與 pickle 類似,也提供了 dumps()、dump()、loads()、load()四個函式,但輸出結果是 JSON 格式的。此外,json 函式作用於 str 物件,而不是 bytes。因此,當輸出或載入時,我們需要建立文字模式的檔案而不是二進位制模式。JSON 只能序列化基本型別,如整數、浮點數和字串,以及簡單的容器,如字典和列表。這些都直接對映到 JSON 形式,不過** JSON 不能表示類、方法或函式。不能用這種格式來傳輸完整的物件。因為接收者通常不是 Python 物件,接收者不能與 Python 以同樣的方式來理解類或方法。**

image

image

image

image

import json

_# 人員資訊列表_
humaninfodic={
    'age'   : 18,
    'name'  : True,
    'gender': 10,
    'email' : 11.1,
}

_# 序列化到檔案中_
with open('test.json', 'w') as fp:
    json.dump(humaninfodic, fp, indent=4)

_# 反序列化檔案中的內容_
with open('test.json', 'r') as fp:
    dic = json.load(fp)
    print(dic)

執行結果如下:

image

image

這裡,需要注意的是,Python 中字典的非字元的值 Key 被轉換成 JSON 字串時都會被轉換為小寫字串,例如 True 會被對映為 true,False 被對映為 false,而 None 會被對映為 null;同時 Python 中的元組在序列化時會被轉換為 array 型別,但是反序列化時,array 型別會被轉化為 Python 中的列表型別。

JSON 和 Python 內建的資料型別對應如下:

JSON 型別 Python 型別
{} dict
[] list
"string" str
1234.56 int 或 float
true/false True/False
null None

如果想要序列化的物件只有資料,我們可以直接序列化物件的__dict__屬性。或者我們也可以針對特定的物件,透過自定義程式碼來建立或解析 JSON 序列化字典。

image

image

在 json 模組中,物件的儲存和載入功能均設有一個可選引數,用以執行特定的自定義操作。具體來說,dumpdumps方法接受一個名為cls(即“class”的簡寫,因其為 Python 的保留關鍵字)的關鍵字引數。當傳遞此引數時,它必須是 JSONEncoder 的一個子類,並且要求重寫了 default 方法。這個方法的設計初衷是接收任意型別的物件作為引數,然後將其轉換為 json 能夠處理的字典型別。如果在處理過程中遇到不知道如何處理的物件型別,可以透過呼叫 super() 方法,使得該物件能夠按照正常的方式進行序列化處理,即按照基本型別進行序列化。

同樣地,loadloads方法也接受一個名為cls的引數,它是JSONDecoder的一個子類。通常情況下,透過object_hook關鍵字傳遞一個函式便足夠了。這個函式的任務是接收一個字典作為引數,並返回一個物件。如果在處理過程中遇到不知道如何處理的字典,可以選擇不進行任何修改,直接將其返回。這種設計使得使用者在進行 json 資料的解析時,能夠更加靈活和方便地處理各種複雜的資料型別。

import json

_# 定義聯絡人類_
class Contact:
    def __init__(self, first, last):
        _# 屬性1,first name是名字_
        self.first = first
        _# 屬性2,last name是姓氏_
        self.last = last
    @property
    def full_name(self):
        return("{} {}".format(self.first, self.last))

_# 自定義序列化編碼器類_
class ContactEncoder(json.JSONEncoder):
    _# default 方法檢查了我們想要序列化的物件型別_
    def default(self, obj):
        _# 如果是聯絡人類,我們手動將其轉換為字典_
        if isinstance(obj, Contact):
            return {
                    _# 傳遞了一個額外的屬性來說明這是一個聯絡人物件_
                    _# 因為沒有其他辦法可以在載入之後知道它的型別_
                    'is_contact': True,
                    'first': obj.first,
                    'last': obj.last,
                    'full': obj.full_name}
        _# 否則,讓其父類來處理序列化(假設它是基本型別,json 知道如何處理)_
        return super().default(obj)

_# 定義一個JSON檔案解碼器函式_
def decode_contact(dic):
    _# 寫一個函式接受字典為引數_
    _# 檢查是否包含 is_contact 變數來決定是否將其轉換為聯絡人_
    if dic.get('is_contact'):
        return Contact(dic['first'], dic['last'])
    else:
        return dic

if __name__ == '__main__':
    c = Contact("John", "Smith")
    data = json.dumps(c, cls=ContactEncoder)
    print(data)
    c = json.loads(data, object_hook=decode_contact)
    print(c.full_name)

執行結果如下:

image

使用 XML 實現序列化 Web 物件

在當今的軟體開發領域,XML 作為一種靈活且強大的標記語言,已經廣泛應用於資料儲存、配置管理、網路傳輸等多個場景。它的可擴充套件性和自描述性讓它成為了不同系統和平臺之間資料交換的理想格式。

XML 指可擴充套件標記語言(eXtensible Markup Language),是一套定義語義標記的規則,這些標記將文件分成許多部件並對這些部件加以標識,它被設計用來傳輸和儲存資料,不用於表現和展示資料。它也是元標記語言,即定義了用於定義其他與特定領域有關的、語義的、結構化的標記語言的句法語言。

如下我們列舉了一個 xml 檔案的例子:

<?xml version="1.0" encoding="utf-8"?>
<catalog>
    <maxid>4</maxid>
    <login username="pytest" passwd='123456'>
        <caption>Python</caption>
        <item id="4">
            <caption>測試</caption>
        </item>
    </login>
    <item id="2">
        <caption>Zope</caption>
    </item>
</catalog>

以上 XML 格式的檔案中,包含了一個名為"catalog"的根元素。根元素下有一個名為"maxid"的子元素,其值為"4"。接著是一個名為"login"的子元素,它包含兩個子元素:一個名為"caption"的子元素,其值為"Python";另一個名為"item"的子元素,其 id 屬性值為"4",包含一個名為"caption"的子元素,其值為"測試"。最後還有一個名為"item"的子元素,其 id 屬性值為"2",包含一個名為"caption"的子元素,其值為"Zope"。

其中書名號圈住的部分“”為標籤,標籤必須成對出現,有開始標籤就需要有結束標籤,例如”“為開始標籤,“”為結束標籤。

一個基本的 XML 文件結構包括以下部分:

結構部分 作用
宣告部分 位於文件的最開始,宣告 XML 的版本和編碼方式。例如:。
根元素 每個 XML 文件都有一個根元素,它包含了所有其他元素。
子元素 根元素內部可以包含多個子元素,子元素可以巢狀並形成樹狀結構。
屬性 元素可以有屬性,屬性提供了關於元素的額外資訊。
文字內容 元素可以包含文字內容。

Python 作為一門簡潔而強大的程式語言,提供了豐富的庫來處理 XML 資料,使得從解析到修改再到建立 XML 文件變得既簡單又高效。Python 有三種方法解析 XML、SAX、DOM,以及 ElementTree。

image

其中,xml.etree.ElementTree(簡稱 ET)提供了一個輕量級的 Pythonic 方式來處理 XML 資料。ET 允許使用者輕鬆地讀取、修改和建立 XML 檔案。由於是標準庫的一部分,因此不需要額外安裝即可使用,這使得它成為處理 XML 資料的一個便捷選擇。

對於 ElementTree 庫來說,常見使用操作包括解析 XML 文件,獲取根元素、遍歷子元素、讀取元素的標籤、文字和屬性,以及如何根據需要獲取或刪除特定元素,以及儲存修改後的 XML 文件。

這裡,對於 XML 檔案僅作為了解使用,並不展開進行講解。

使用 YAML 實現序列化 Web 物件

XML 檔案雖然功能強大,但由於其標記語言的特性,通常不太易於閱讀。相反,XML 在需要對驗證、架構和名稱空間進行精細控制的複雜專案中表現出色。與 XML 相比,YAML 則專注於資料格式化,以可讀程式碼的形式呈現,其內聯風格與 JSON 頗為相似。YAML 旨在提供更為直觀和易讀的資料表示方式,以滿足不同場景下的需求。

YAML,作為一種高度人性化的資料序列化語言,能夠與當前主流的程式語言無縫整合。其名稱“YAML”來源於“YAML Ain't a Markup Language”(YAML 不是一種標記語言)的遞迴縮寫,這一命名既體現了其獨特性,又突顯了其與眾不同的設計理念。

YAML 的語法結構與其他高階語言相似,能夠輕鬆表達清單、雜湊表以及標量等多種資料形態。它巧妙地利用空白符號進行縮排,並大量依賴外觀特徵來展示資料結構的層次關係。這種設計使得 YAML 特別適合用於編輯資料結構、編寫各種配置檔案、列印除錯資訊以及呈現檔案大綱等場景。

此外,YAML 配置檔案通常以“.yml”作為檔案字尾,這一命名約定有助於使用者快速識別和管理 YAML 檔案。

它的基本語法規則如下:

(1)大小寫敏感;

(2)使用縮排表示層級關係;

(3)縮排時不允許使用 Tab 鍵,只允許使用空格。

(4)縮排的空格數目不重要,只要相同層級的元素左側對齊即可

YAML 支援的資料結構有三種:

(1)物件:鍵值對的集合,又稱為對映(mapping)/ 雜湊(hashes) / 字典(dictionary);

(2)陣列:一組按次序排列的值,又稱為序列(sequence) / 列表(list);

(3)純量(scalars):單個的、不可再分的值。

在 Python 中,有多個庫可用於解析和生成 YAML 資料,其中最常用的是 PyYAML。

image

這裡,我們以下列名為 config.yaml 的 YAML 配置檔案為例,簡單講解一下 YAML 檔案語法:

_# 配置檔案示例_
  
_# 伺服器配置_
server:  
  host: localhost  
  port: 8080  

_# 資料庫配置_
database:  
  type: MySQL  
  host: 127.0.0.1  
  port: 3306  
  username: root  
  password: password  

_# 日誌記錄_
logging:  
  level: info  
  file: app.log

_# 應用配置_
app:
  debug: true
  log_level: info

這裡,該檔案使用井號(#)表示註釋,使用縮排表示層級關係,並且使用冒號(:)分隔鍵和值。

這個 YAML 檔案包含以下幾個部分:

(1)伺服器配置:包括主機名和埠號;

(2)資料庫配置:包括資料庫型別、主機名、埠號、使用者名稱和密碼;

(3)日誌記錄:包括日誌級別和日誌檔案路徑;

(4)應用配置:包括是否開啟除錯模式和日誌級別。

XML 和 YAML 檔案都有一些複雜的特徵,如果被惡意利用,就可以允許在主機上執行任意命令。這與 JSON 檔案不同,JSON 不支援任何可執行程式碼,只有資料可以被序列化。

image

相關文章