我一直在撰寫代碼,以決議和提取由機器人發送的訊息中的資訊。雖然只有幾種不同的訊息,但是每一種訊息都包含了我感興趣的各種資訊,我正在努力尋找在代碼中作為物件處理它們的最佳方法。
如果我使用Haskell,我會簡單地創建一個Message型別,并為每一種訊息定義一個定制的建構式
data Message = Greeting Foo Bar | Warning Yadda Yadda | ...。
這是一個非常好的、干凈的方法,既可以把它們都放在同一個型別下,又可以很容易地把訊息的種類區分開。
如何以 OOP 友好的方式(或更好的方式,Pythonic)來設計物件類以達到這一效果? 我想到了兩種方法,即:
定義一個基類
Message,并為每一種訊息子類化。優點:在概念上很簡潔。缺點:大量的模板代碼,而且它并沒有真正使代碼具有可讀性,也沒有明確不同訊息類之間的關系。定義一個通用類
Message,它代表每一種訊息型別。它將有一個屬性.type來區分訊息種類,它的__init__函式將相應地實體化適合訊息型別的屬性。優點:代碼簡單,實用。缺點:讓類的屬性如此不可預測似乎是一種不好的做法,而且總的來說感覺不對。
但是我對這兩者都不完全滿意。雖然我意識到這只是一個小程式,但我想我正在利用它作為一個機會來學習更多關于使用抽象和軟體架構的知識。誰能給我指條明路?
uj5u.com熱心網友回復:
對于訊息類的設計,我會使用dataclasses來盡量減少模板。你可以把注意力完全集中在欄位上:
from dataclasses import dataclass
class 訊息。
# 常見的訊息方法
class Greeting(Message)。
foo: str: foo.
bar。int。
class Warning(Message)。
yadda: list[str]
對于一個簡單的專案,通常沒有更多的需要。你可以在Message基類中添加一個@classmethod工廠,以幫助生成特定的訊息型別,并且Message本身也可以是一個@dataclass,如果不同型別之間有共同的屬性。
這就是說,一旦您開始考慮序列化和反序列化的要求,使用一個type欄位作為enum 可以有幫助。
為了說明這一點。對于目前一個包括自動OpenAPI 3.1檔案的RESTFul API專案,我們正在使用Marshmallow來處理從JSON到JSON的翻譯,marshmallow-dataclasses以避免重復定義模式和驗證,以及marshmallow-oneofschema以反映一個多型的模式,用于根據型別不同的類層次,就像你的Message例子。
使用第三方庫會限制您的選擇,因此我使用了元編程(主要是class.__init_subclass__和Generic型別注釋)來簡化定義這種以列舉為鍵的多型型別層次的可能性。
你的訊息型別將被這樣表達:
你的訊息型別將被這樣表達:
class MessageType(enum.Enum)。
greeting = "greeting"
warning = "警告"/span>
# ...
@dataclass
class _BaseMessage(PolymorphicType[MessageType])。
type: 訊息型別
# ...: MessageType
@dataclass
class Greeting(_BaseMessage, type_key=MessageType.greeting)。
foo: str
吧。int。
@dataclass
class Warning(_BaseMessage, type_key=MessageType.warning)。
yadda: list[str]
MessageSchema = _BaseMessage.OneOfSchema("MessageSchema"/span>)
之后,使用MessageSchema.load()從JSON中加載訊息,根據字典中的"type"鍵產生一個特定的實體,例如:
message = MessageSchema.load({"type"/span>: "greeting"/span>, "foo"/span>: "spam", "bar": 42})
isinstance(message, Greeting) # True。
同時MessageSchema.dump()無論輸入型別如何,都能得到合適的JSON輸出:
message = Warning([42, 117] )
MessageSchema.dump(message) # {"type": "warning", "yadda": [42, 117]}
正是在這里使用了enum,使得整合效果最佳;PolymorphicType是一個自定義類,它處理了大部分繁重的作業,使最后的_BaseMessage.OneOfSchema()呼叫發揮作用。你不必必須使用元編程來實作最后一部分,但對我們來說,它減少了大部分marshmallow-oneschema的模板。
此外,我們得到了反映每個特定訊息型別的 OpenAPI 架構,像 Redocly 這樣的檔案工具知道如何處理:
components:
schemas:
訊息:
oneOf:/span>
- $ref: '#/components/schemas/Greeting'/span>
- $ref: '#/components/schemas/Warning'/span>
鑒別器:
propertyName: type
映射:
問候: '#/components/schemas/Greeting'/span>
警告: '#/components/schemas/Warning'/span>
招呼:
型別: 物件
屬性:
型別:
型別: string
default: greeting
福:
型別: string
酒吧:
型別: integer
警告:
型別: object
屬性:
型別:
型別: string
default: warning
yadda:
型別: array
專案:
型別: string
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/313406.html
標籤:
