我有一个关于数据类的相当基本的问题。如果我有一个事件字典作为数据传递给数据类,那么通常使用该类来解析我需要的数据是否可以很好地利用数据类?或者使用它来处理条件,以便根据传入的数据返回正确的数据。
@dataclass
class Event:
data: dict[str,str]
def type(self):
return self.data["detail"]["eventName"]
我刚刚开始使用数据类,并发现我的代码通常是多么糟糕,因为它是匆忙编写的,很少考虑开闭扩展性或抽象。所以我试图弄清楚组合的根源,以及何时何地事物应该耦合以及何时不应该耦合。 IE。与以下相反:
@dataclass
class Event:
type: str
Event(e["detail"]["eventName"])
拥有这样的东西对我来说很有意义
@dataclass
class Event:
type: str
name: str
id: int
但是,如果访问 id 的字典路径发生变化,或者更相关的是,如果路径根据类型而不同怎么办,这实际上是我遇到的问题之一。如果您创建 class EventType1
class EventType2
我仍然需要尝试事件类型 1 来查看构造函数是否有效,因为路径可能有效,然后转到 2。似乎就像我遗漏了一些东西,我正在用糟糕的设计替换糟糕的设计,因为你需要为每种可能的事件类型创建一个类。人们的想法是什么?这是数据类的错误使用吗?是否应该将所有索引从类中取出并在其他地方完成?
编辑---
我决定添加一个更具体的示例来说明我的问题。我使用 EventData
来抽象事件格式的概念,而 data1 和 data2 变得无关紧要,因为我实际上正在使用 EventData
。我仍然需要根据事件格式知道要使用此玩具示例 GitPush
或 CreatePullRequest
中的哪个 EventType 构造函数。因为我可以将此接口(interface)扩展到许多事件,并且使用 EventData
的代码并不关心。
class Event(ABC):
""" implement me """
@abstractmethod
def type(self) -> str:
pass
@abstractmethod
def repository(self) -> str:
pass
# event type 1
class GitPush(Event):
""" implemented supported event type """
def __init__(self, data):
self.data = data
@property
def type(self) -> str:
return self.data["detail"]["eventName"]
@property
def repository(self) -> str:
return self.data["detail"]["additionalEventData"]["repositoryName"]
# event type 2
class PullRequest(Event):
""" implemented supported event type """
def __init__(self, data):
self.data = data
@property
def type(self) -> str:
return self.data["detail"]["eventName"]
@property
def repository(self) -> str:
return self.data["detail"]["requestParameters"]["targets"][0]["repositoryName"]
@dataclass
class EventData:
data: Event
@property
def type(self):
return self.data.type
@property
def repository(self):
return self.data.repository
event = GitPush(data1)
data = EventData(event)
print(data.repository)
event = PullRequest(data2)
data = EventData(event)
print(data.repository)
我对 EventType 的想法感到困惑,以及它是否可以或应该被抽象,或者这是否只是一个可扩展点,新事件可以满足实现要求。
event = EventType(some_event)
data = EventData(event)
print(data.repository)
除了使用条件之外,我想不出任何其他方法:
if some_event["detail"]["eventName"] == "Type1":
e = Type1(some_event)
if some_event["detail"]["eventName"] == "Type2":
e = Type2(some_event)
data = EventData(e)
print(data.repository)
最佳答案
我将与您分享我在这种情况下经常使用的模式。问题是我们有一些类似 JSON 格式的不受信任的数据,我们希望将其存储在良好的数据结构中。这是一种很好的本能,尽早摆脱困惑的业务,并能够在程序的其余部分假设数据的形状良好(请参阅 Parse, don't Validate ,这是一篇关于该主题的优秀文章)。
这是我过去所做的事情。
@dataclass
class Event:
type: str
name: str
id: int
@classmethod
def from_json(data):
return Event(
type=data["detail"]["eventName"],
name=data["name"],
id=data["id"],
)
你有一个数据类。这是一个真实的、真正的@dataclass
,任何愿意的人都可以直接构造它的实例。但预期的入口点是工厂函数from_json
,它获取您的字典并将其解析为Event
对象。如果数据格式发生变化,只需更改该函数即可。
如果您应该执行任何验证(也许 ID 必须为非负数或其他内容,或者名称具有最大长度),那么您也可以在 from_json
中执行此操作,并抛出异常输入错误。显然,您想要记录此行为,但仍然可以在这个地方进行检查,即在涉及您正在交谈的任何 API 的应用程序端点处。
如果您有多个不同事件类型,那么您可以有一个父(抽象)类Event
及其所有(具体)子类,以及父类类可以提供一个@classmethod
,它根据数据的形状构造适当子类的实例。仍然是一个入口点。
关于python 数据类作为 oop 抽象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72519081/