代码之家  ›  专栏  ›  技术社区  ›  Ivo Flipse

如何在Python中设计类?

  •  141
  • Ivo Flipse  · 技术社区  · 15 年前

    我在以前的问题上得到了一些非常棒的帮助 for detecting paws toes within a paw ,但所有这些解决方案一次只能用于一个度量。

    Now I have data 这包括:

    • 大约30只狗;
    • 每个都有24个测量值(分为多个子组);
    • 每个测量至少有4个触点(每个棘爪一个)和
      • 每个触点分为5个部分,
      • 有几个参数,如接触时间、位置、总力等。

    alt text

    很明显,把所有的东西都放在一个大对象中并不能切断它,所以我想我需要使用类而不是当前的函数集合。但是尽管我已经阅读了关于类的学习python的章节,但是我没有将它应用到我自己的代码中。( GitHub link )

    我也觉得处理所有的数据很奇怪 每一个 是时候我想知道一些信息了。一旦我知道了每个爪子的位置,就没有理由再计算这个了。此外,我想比较同一只狗的所有爪,以确定哪个接触属于哪个爪(前/后,左/右)。如果我继续只使用函数,这将变得一团糟。

    因此,现在我正在寻找关于如何创建类的建议,这些类将允许我处理我的数据。( link to the zipped data of one dog )以明智的方式。

    6 回复  |  直到 7 年前
        1
  •  429
  •   S.Lott    15 年前

    如何设计一个类。

    1. 把单词写下来。你开始这样做了。有些人不知道为什么会有问题。

    2. 将您的一组单词展开成简单的语句,说明这些对象将要做什么。也就是说,写下你在这些事情上要做的各种计算。你列出的30只狗,24个测量值,4个接触点,以及每个接触点的几个“参数”很有趣,但只是故事的一部分。你的“每个爪的位置”和“比较同一只狗的所有爪以确定哪个接触属于哪个爪”是对象设计的下一步。

    3. 给名词加下划线。说真的。有些人对这一点的价值表示怀疑,但我发现对于第一次使用OO的开发人员来说,这是有帮助的。给名词加下划线。

    4. 复习名词。像“参数”和“度量”这样的一般性名词需要替换为应用于问题域中问题的特定具体名词。细节有助于澄清问题。泛型只是省略了细节。

    5. 对于每个名词(“contact”、“paw”、“dog”等),写下该名词的属性以及该对象所从事的动作。别抄近路。每个属性。”例如,数据集包含30只狗是很重要的。

    6. 对于每个属性,确定这是与已定义名词的关系,还是与其他类型的“原始”或“原子”数据(如字符串、浮点或不可约的数据)的关系。

    7. 对于每一个动作或操作,你必须确定哪一个名词有责任,哪一个名词只参与其中。这是一个“可变性”的问题。有些对象会更新,有些则不会。可变对象必须对它们的突变负全部责任。

    8. 此时,可以开始将名词转换为类定义。有些集合名词是列表、字典、元组、集合或命名的两个名词,您不需要做很多工作。其他类更复杂,可能是因为派生数据复杂,也可能是因为执行了某些更新/突变。

    不要忘记使用UnitTest单独测试每个类。

    而且,没有法律规定类必须是可变的。例如,在您的案例中,几乎没有可变数据。您所拥有的是由源数据集的转换函数创建的派生数据。

        2
  •  22
  •   endolith    7 年前

    以下建议(类似于@s.lott的建议)来自本书, Beginning Python: From Novice to Professional

    1. 写下你的问题描述(问题应该怎么做?).在所有名词、动词和形容词下面划线。

    2. 浏览名词,寻找潜在的类。

    3. 浏览动词,寻找可能的方法。

    4. 浏览形容词,寻找潜在的属性

    5. 为类分配方法和属性

    为了完善课程,本书还建议我们可以做以下工作:

    1. 写下一套 用例 _)如何使用您的程序的场景。尝试覆盖所有功能。

    2. 一步一步地考虑每一个用例,确保我们需要的一切都被覆盖。

        3
  •  13
  •   Les Nightingill    12 年前

    我喜欢TDD方法… 因此,首先要为你想要的行为编写测试。并编写通过的代码。在这一点上,不要太担心设计,只要得到一个测试套件和通过测试的软件。如果你最终只得到一个大而难看的类,使用复杂的方法,不要担心。

    有时,在这个初始过程中,您会发现一个很难测试的行为,需要分解,只是为了测试性。这可能是一个提示,表明需要单独的类。

    那么有趣的部分…重构。有了工作软件后,您可以看到复杂的部分。通常,少量的行为会变得明显,这意味着一个新的类,但如果不是,只需寻找简化代码的方法。提取服务对象和值对象。简化你的方法。

    如果您正确使用Git(您使用的是Git,不是吗?)在重构过程中,您可以非常快地对某些特定的分解进行试验,然后放弃它,如果它不能简化事情的话,则返回。

    通过首先编写经过测试的工作代码,您应该对问题域有一个非常深入的了解,而设计第一方法是不容易做到的。编写测试和代码会让你摆脱“我从哪里开始”的瘫痪。

        4
  •  3
  •   Spacedman    15 年前

    OO设计的全部思想是使代码映射到您的问题上,因此,例如,当您想要狗的第一步时,您可以执行如下操作:

    dog.footstep(0)
    

    现在,对于您的情况,您可能需要读取原始数据文件并计算足迹位置。所有这些都可以隐藏在footstep()函数中,以便它只发生一次。比如:

     class Dog:
       def __init__(self):
         self._footsteps=None 
       def footstep(self,n):
         if not self._footsteps:
            self.readInFootsteps(...)
         return self._footsteps[n]
    

    [这现在是一种缓存模式。当它第一次读取足迹数据时,随后它只从自身获取数据。_footstep。]

    但是,是的,正确地进行OO设计是很困难的。更多地考虑您想要对数据做的事情,这将告诉您需要哪些方法来应用于哪些类。

        5
  •  2
  •   Evan Moran    12 年前

    写出你的名词、动词、形容词是个很好的方法,但我更愿意把课堂设计看作是提问。 应该隐藏哪些数据 ?

    想象你有一个 Query 对象和 Database 对象:

    这个 查询 对象将帮助您创建和存储一个查询——store是这里的键,因为函数可以帮助您轻松地创建一个查询。也许你可以留下来: Query().select('Country').from_table('User').where('Country == "Brazil"') . 语法完全不重要——这是你的工作!--关键是目标是帮助你 隐藏一些东西 ,在这种情况下,存储和输出查询所需的数据。对象的力量来自于使用它的语法(在本例中是一些巧妙的链接),而不需要知道它存储了什么才能使它工作。如果做得对 查询 对象可以为多个数据库输出查询。它在内部存储特定的格式,但在输出时可以很容易地转换为其他格式(postgres、mysql、mongodb)。

    现在让我们考虑一下 数据库 对象。这里面藏着什么?很明显,它不能存储数据库的全部内容,因为这就是为什么我们有一个数据库!那有什么意义呢?目标是 隐藏数据库的工作方式 来自使用 数据库 对象。好的类将在操作内部状态时简化推理。为了这个 数据库 对象可以隐藏网络调用的工作方式,或批查询或更新,或提供缓存层。

    问题是这个 数据库 物体很大。它表示如何访问数据库,因此在封面下它可以做任何事情。很明显,根据您的系统,网络、缓存和批处理是很难处理的,因此将它们隐藏起来会非常有帮助。但是,正如许多人会注意到的那样,数据库是非常复杂的,而且离原始的DB调用越远,就越难调整性能并理解工作原理。

    这是OOP的基本权衡。如果选择正确的抽象,它会使编码更简单(字符串、数组、字典),如果选择的抽象太大(数据库、emailmanager、networkingmanager),它可能会变得太复杂,无法真正理解它的工作方式或期望的内容。目标是 隐藏复杂性 但有些复杂性是必要的。一个很好的经验法则是从避免 Manager 对象,而是创建类似 structs --他们所做的就是保存数据,用一些辅助方法来创建/操作数据,使您的生活更轻松。例如,在 EmailManager 从调用的函数开始 sendEmail 这需要 Email 对象。这是一个简单的起点,代码非常容易理解。

    对于您的示例,请考虑需要将哪些数据放在一起才能计算您要查找的内容。例如,如果你想知道一只动物走了多远,你可以 AnimalStep AnimalTrip (animalsteps集合)类。既然每一次旅行都有所有的步骤数据,那么它就应该能够弄清楚关于它的事情,也许 AnimalTrip.calculateDistance() 有道理。

        6
  •  2
  •   cyborg    12 年前

    在略读了你的链接代码之后,我觉得你过得更好 在这一点上设计一个狗类。相反,你应该使用 Pandas dataframes . 数据帧是一个带有列的表。您的数据框架将包含以下列: dog_id , contact_part , contact_time , contact_location 等。 熊猫在幕后使用numpy数组,它有许多方便的方法:

    • 选择一只狗,例如: my_measurements['dog_id']=='Charly'
    • 保存数据: my_measurements.save('filename.pickle')
    • 考虑使用 pandas.read_csv() 而不是手动读取文本文件。