How do I design a class in Python?
在我之前的问题上,我有一些非常棒的帮助,可以检测到爪内的爪和脚趾,但是所有这些解决方案一次只能用于一个测量。
现在我得到的数据包括:
- 约30只狗;
- 每个都有24个测量值(分为多个子组);
- 每个测量至少有4个触点(每个棘爪一个)和
- 每个触点分为5个部分,
- 有几个参数,如接触时间、位置、总力等。
很明显,把所有的东西都放在一个大对象中并不能切断它,所以我想我需要使用类而不是当前的函数集合。但是,尽管我已经阅读了关于类的学习python的章节,但是我没有将它应用到我自己的代码中(github链接)
我也觉得每次我想得到一些信息时处理所有的数据是很奇怪的。一旦我知道了每个爪子的位置,就没有理由再计算这个了。此外,我想比较同一只狗的所有爪,以确定哪个接触属于哪个爪(前/后,左/右)。如果我继续只使用函数,这将变得一团糟。
因此,现在我正在寻找关于如何创建类的建议,这些类可以让我以一种合理的方式处理我的数据(链接到一只狗的压缩数据)。
如何设计一个类。
把单词写下来。你开始这样做了。有些人不知道为什么会有问题。
将您的一组单词展开成简单的语句,说明这些对象将要做什么。也就是说,写下你在这些事情上要做的各种计算。你列出的30只狗,24个测量值,4个接触点,以及每个接触点的几个"参数"很有趣,但只是故事的一部分。你的"每个爪的位置"和"比较同一只狗的所有爪以确定哪个接触属于哪个爪"是对象设计的下一步。
给名词加下划线。说真的。有些人对这一点的价值表示怀疑,但我发现对于第一次使用OO的开发人员来说,这是有帮助的。给名词加下划线。
复习名词。像"参数"和"度量"这样的一般性名词需要替换为应用于问题域中问题的特定具体名词。细节有助于澄清问题。泛型只是省略了细节。
对于每个名词("contact"、"paw"、"dog"等),写下该名词的属性以及该对象所从事的动作。别抄近路。每个属性。"例如,数据集包含30只狗是很重要的。
对于每个属性,确定这是与已定义名词的关系,还是与其他类型的"原始"或"原子"数据(如字符串、浮点或不可约的数据)的关系。
对于每一个动作或操作,你必须确定哪一个名词有责任,哪一个名词只参与其中。这是一个"可变性"的问题。有些对象会更新,有些则不会。可变对象必须对它们的突变负全部责任。
此时,可以开始将名词转换为类定义。有些集合名词是列表、字典、元组、集合或命名的两种,您不需要做很多工作。其他类更复杂,可能是因为派生数据复杂,也可能是因为执行了某些更新/突变。
不要忘记使用UnitTest单独测试每个类。
而且,没有法律规定类必须是可变的。例如,在您的案例中,几乎没有可变数据。您所拥有的是由源数据集的转换函数创建的派生数据。
以下建议(类似于@s.lott的建议)来自于本书,开始于python:从新手到专业人士
Write down a description of your problem (what should the problem do?). Underline all the nouns, verbs, and adjectives.
Go through the nouns, looking for potential classes.
Go through the verbs, looking for potential methods.
Go through the adjectives, looking for potential attributes
Allocate methods and attributes to your classes
为了完善课程,本书还建议我们可以做以下工作:
Write down (or dream up) a set of use cases—scenarios of how your program may be used. Try to cover all the functionally.
Think through every use case step by step, making sure that everything we need is covered.
我喜欢TDD方法…因此,首先要为你想要的行为编写测试。并编写通过的代码。在这一点上,不要太担心设计,只要得到一个测试套件和通过测试的软件。如果你最终只得到一个大而难看的类,使用复杂的方法,不要担心。
有时,在这个初始过程中,您会发现一个很难测试的行为,需要分解,只是为了测试性。这可能是一个提示,说明需要单独的类。
那么有趣的部分…重构。有了工作软件后,您可以看到复杂的部分。通常,少量的行为会变得明显,这意味着一个新的类,但如果不是,只需寻找简化代码的方法。提取服务对象和值对象。简化你的方法。
如果您正确使用Git(您使用的是Git,对吗?)在重构过程中,您可以非常快地对某些特定的分解进行试验,然后放弃它,如果它不能简化事情的话,则返回。
通过首先编写经过测试的工作代码,您应该对问题域有一个非常深入的了解,而设计第一方法是不容易做到的。编写测试和代码会让你摆脱"我从哪里开始"的瘫痪。
OO设计的全部思想是使代码映射到您的问题上,因此,例如,当您想要狗的第一步时,您可以执行如下操作:
1 | dog.footstep(0) |
现在,对于您的情况,您可能需要读取原始数据文件并计算足迹位置。所有这些都可以隐藏在footstep()函数中,以便它只发生一次。类似:
1 2 3 4 5 6 7 | class Dog: def __init__(self): self._footsteps=None def footstep(self,n): if not self._footsteps: self.readInFootsteps(...) return self._footsteps[n] |
[这现在是一种缓存模式。当它第一次读取足迹数据时,随后它只从自身获取数据。_footstep。]
但是,是的,正确地进行OO设计是很困难的。更多地考虑您想要对数据做的事情,这将告诉您需要哪些方法来应用于哪些类。
在略读了你链接的代码之后,在我看来,现在最好不要设计一个dog类。相反,您应该使用熊猫和数据帧。数据帧是一个带有列的表。您的数据帧将具有以下列:
- 选择一只狗,例如:
my_measurements['dog_id']=='Charly' 。 - 保存数据:
my_measurements.save('filename.pickle') 。 - 考虑使用
pandas.read_csv() ,而不是手动读取文本文件。
写出你的名词、动词、形容词是一个很好的方法,但我更喜欢把课堂设计看作是在问问题:应该隐藏什么数据?
假设你有一个
现在让我们考虑一下
问题是这个
这是OOP的基本权衡。如果选择正确的抽象,它会使编码更简单(字符串、数组、字典),如果选择的抽象太大(数据库、emailmanager、networkingmanager),它可能会变得太复杂,无法真正理解它的工作方式或期望的内容。目标是隐藏复杂性,但有些复杂性是必要的。一个好的经验法则是从避免
对于您的示例,请考虑需要将哪些数据放在一起才能计算您要查找的内容。例如,如果你想知道一只动物走了多远,你可以上