python 数据类作为 oop 抽象

标签 python oop

我有一个关于数据类的相当基本的问题。如果我有一个事件字典作为数据传递给数据类,那么通常使用该类来解析我需要的数据是否可以很好地利用数据类?或者使用它来处理条件,以便根据传入的数据返回正确的数据。

@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。我仍然需要根据事件格式知道要使用此玩具示例 GitPushCreatePullRequest 中的哪个 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/

相关文章:

python - 在自定义keras层的调用函数中传递附加参数

java - 在CRUD中执行C的方法中,它应该返回什么?

c# - 'GET OR SET ACCESSOR EXPECTED' 是什么意思?

Javascript 时钟,落后几分钟

c# - 如何从静态函数内部获取/设置成员变量?

python - 列表,元组和统计程序尝试-除 block 错误

python - Python 是否允许一个类方法有多个输入参数?

python - 有人知道如何在 Python 中打开/关闭大写锁定吗?

python - 如何将 networkX 图导入到 neo4j?

c++ - 更改 LinkedList 以接受对象而不是 Int?