我如何在Python中devise一个类?
对于以前的问题,我已经有了一些非常棒的帮助来检测爪子的爪子和脚趾 ,但是所有这些解决scheme一次只能用于一次测量。
现在我有数据组成了:
- 大约30只狗;
- 每个有24个测量值(分成几个子组);
- 每个测量至less有4个触点(每个爪子一个)
- 每个联系人分为5个部分
- 有几个参数,如接触时间,位置,总力等
显然,将所有东西都放在一个大对象中是不会削减的,所以我想我需要使用类而不是当前的函数。 但是,即使我已经阅读了学习Python关于类的章节,但我没有将它应用到我自己的代码( GitHub链接 )
每次我想知道一些信息时,我也觉得处理所有的数据是相当奇怪的。 一旦我知道每个爪子的位置,我没有理由再计算一次。 此外,我想比较同一只狗的所有爪子,以确定哪个接触属于哪个爪(前/后,左/右)。 如果我继续使用函数,这将变得一团糟。
所以现在我正在寻求build议,以便如何创build能够以合理的方式处理我的数据( 链接到一只狗的压缩数据 )的类。
如何devise一个class级。
-
写下单词。 你开始这样做了。 有些人不知道为什么他们有问题。
-
把你的单词扩展成关于这些对象将要做什么的简单陈述。 也就是说,写下你将在这些事情上做的各种计算。 你的30条狗的短名单,24个测量,4个联系,每个接触的几个“参数”是有趣的,但只有一部分的故事。 你的“每个爪子的位置”和“比较同一只狗的所有爪子以确定哪个接触属于哪个爪子”是对象devise中的下一步。
-
强调名词。 认真。 有些人辩论这个价值,但我发现这对首次OO开发人员有帮助。 强调名词。
-
复习名词。 像“参数”和“测量”这样的通用名词需要用适用于问题域的具体名词来替代。 具体有助于澄清这个问题。 generics只是简单的细节。
-
对于每个名词(“接触”,“爪子”,“狗”等),记下该名词的属性以及该对象所从事的动作。 不要把这个缩短。 每个属性。 “数据集包含30个狗”例如是重要的。
-
对于每个属性,确定这是否与定义的名词或某种其他types的“原始”或“primefaces”数据如string或浮点数或某种不可约的关系。
-
对于每个动作或操作,你必须确定哪个名词有责任,哪些名词仅仅参与。 这是一个“可变性”的问题。 一些对象得到更新,而另一些则没有。 可变的对象必须对其突变负全部责任。
-
此时,您可以开始将名词转换为类定义。 一些集体名词是名单,词典,元组,集合或命名集合,你不需要做太多的工作。 其他类更复杂,要么是因为复杂的派生数据,要么是因为执行了一些更新/突变。
不要忘记用unittest隔离testing每个类。
而且,没有法律规定class级必须是可变的。 就你而言,例如,你几乎没有可变数据。 你有什么是由源数据集的转换函数创build的派生数据。
以下build议(类似于@ S.Lott的build议)来自“ Python入门:从新手到专业”一书
-
写下你的问题的描述(问题该怎么办?)。 强调所有的名词,动词和形容词。
-
通过名词,寻找潜在的课程。
-
通过动词,寻找潜在的方法。
-
通过形容词,寻找潜在的属性
-
将方法和属性分配给您的类
为了完善课堂,本书还build议我们可以做到以下几点:
-
记下(或者梦想)一组用例—如何使用你的程序。 尽量涵盖所有的function。
-
一步一步地思考每一个用例,确保我们所需要的一切都被覆盖。
我喜欢TDD的方法…所以,开始写testing你想要的行为。 并编写通过的代码。 在这一点上,不要太担心devise,只需要获得一个testing套件和软件。 不要担心,如果你最终得到一个单一的丑陋的类,复杂的方法。
有时候,在这个初始的过程中,你会发现一个难以testing的行为,需要分解,只是为了可testing性。 这可能是一个暗示,一个单独的类是必要的。
那么有趣的部分…重构。 你有工作软件后,你可以看到复杂的作品。 通常情况下,一小部分行为将变得明显,表明一个新的类,但如果没有,只是寻找方法来简化代码。 提取服务对象和值对象。 简化你的方法。
如果你正确地使用git(你使用的是git,不是吗?),你可以在重构过程中很快地尝试一些特定的分解,然后放弃它,如果不能简化的话就回复。
通过首先编写经过testing的工作代码,您应该深入了解使用“先devise优先”方法无法轻松获得的问题领域。 编写testing和代码推动你过去那个“我从哪里开始”瘫痪。
OOdevise的整个想法是让你的代码映射到你的问题上,所以当你想要一只狗的第一个脚步的时候,你可以这样做:
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]
[这现在是一种caching模式。 第一次它读取脚步数据,随后的时间它只是从self._footsteps得到它]。
但是,是的,正确的OOdevise可能会非常棘手。 想想更多关于你想要对你的数据做什么的事情,这将告诉你需要什么方法来应用到什么类。
写出你的名词,动词,形容词是一个很好的方法,但是我更喜欢把课堂devise看作是提出什么样的问题隐藏的问题。
想象一下你有一个Query
对象和一个Database
对象:
Query
对象将帮助您创build和存储查询 – 存储,在这里是关键,因为函数可以帮助您轻松地创build查询。 也许你可以留下来: Query().select('Country').from_table('User').where('Country == "Brazil"')
。 它的语法无关紧要 – 这是你的工作! – 关键是对象正在帮助你隐藏一些东西 ,在这种情况下是存储和输出查询所必需的数据。 对象的力量来自使用它的语法(在这种情况下是一些巧妙的链接),而不需要知道它存储的是什么使其工作。 如果正确完成, Query
对象可以为多个数据库输出查询。 它在内部将存储一个特定的格式,但可以很容易地转换为其他格式输出(Postgres,MySQL,MongoDB)。
现在让我们考虑一下Database
对象。 这是什么隐藏和存储? 很明显,它不能存储数据库的全部内容,因为这就是为什么我们有一个数据库! 那么有什么意义呢? 目标是隐藏数据库如何使用Database
对象的人。 良好的类将在操纵内部状态时简化推理。 对于这个Database
对象,您可以隐藏networking调用的工作方式,批量查询或更新,或提供caching层。
问题是这个Database
对象是巨大的。 它代表了如何访问一个数据库,所以它可以做任何事情。 显然,networking,caching和批处理很难处理,取决于你的系统,所以把它们隐藏起来将是非常有帮助的。 但是,正如很多人会注意到的,一个数据库是非常复杂的,而且越靠近原始的DB数据库,调整性能和理解事情的工作就越困难。
这是面向对象的基本权衡。 如果你select了正确的抽象,它会使编码更简单(string,数组,字典),如果你select一个太大的抽象(数据库,电子邮件pipe理器,networkingpipe理器),可能会变得太复杂,不能真正理解它是如何工作的,期望。 目标是隐藏复杂性 ,但一些复杂性是必要的。 一个好的经验法则是开始避免Manager
对象,而是创build类似于structs
类 – 他们所做的只是保存数据,还有一些辅助方法来创build/操作数据,使您的生活更轻松。 例如,在EmailManager
的情况下,启动一个名为sendEmail
的函数,该函数需要一个Email
对象。 这是一个简单的起点,代码很容易理解。
至于你的例子,想想什么样的数据需要在一起来计算你在找什么。 例如,如果您想知道动物行走的距离,可以使用AnimalStep
和AnimalTrip
( AnimalTrip
集合)类。 现在每个Trip都有Step数据,那么它应该能够计算出有关它的信息,也许AnimalTrip.calculateDistance()
是有意义的。
在浏览链接的代码之后,在我看来,在这一点上你最好不要devise一个Dog类。 相反,你应该使用pandas和数据框 。 数据框是一个包含列的表格。 你的数据dog_id
会有如下的列: dog_id
, contact_part
, contact_time
, contact_location
等等dog_id
在后台使用Numpy数组,它有许多方便的方法:
- select一个狗,例如:
my_measurements['dog_id']=='Charly'
- 保存数据:
my_measurements.save('filename.pickle')
- 考虑使用
pandas.read_csv()
而不是手动读取文本文件。