其他接口的实现也与此类似 。最后,run 函数负责执行所有查询,返回最终结果;
完成上述实现后执行测试,确保我们的实现是正确的 。
在前面我们说过 , 延迟查询与主动查询相比,最大的优势是对于许多查询可以按需要访问,不需要每个步骤都返回完整结果,从而提高性能,节约查询时间 。比如说,对于下面的查询:
以上查询的意思是从孙辈中找到一个符合条件的节点即可 。对该查询而言,主动查询会在调用outcome('son') 时就遍历所有节点,哪怕最后一步只需要第一个结果 。而延迟查询为了提高效率,应在找到符合条件的结果后立即停止 。
目前我们尚未实现take 方法 。老规矩,先添加测试:
主动查询的take 实现比较简单 , 我们只要从结果中返回前 n 条记录:
延迟查询的实现要复杂一些 。为了避免不必要的查找,返回结果不应该是完整的列表( list ),而应该是个按需返回的可迭代对象,我们用内置函数 next 来依次返回前 n 个结果:
写完后运行测试,确保它们是正确的 。
从外部接口看,主动查询和延迟查询几乎是完全相同的,所以用单纯的数据测试很难确认后者的效率一定比前者高,用访问时间来测试也并不可靠 。为了测试效率,我们引入一个节点访问次数的概念,如果延迟查询效率更高的话,那么它应该比主动查询访问节点的次数更少 。
为此,编写如下测试:
我们为Dagoba 类添加一个成员来记录总的节点访问次数,以及两个辅助方法,分别用于获取和重置访问次数:
然后浏览代码 , 查找修改点 。增加计数主要在从边查找节点的时候,因此修改部分如下:
此外还有income/outcome 方法 , 修改都很简单,这里就不再列出 。
实现后再次运行测试 。测试通过 , 表明延迟查询确实在效率上优于主动查询 。
不像关系数据库的结构那样固定,图的形式可以千变万化,查询机制也必须足够灵活 。从原理上讲,所有查询无非是从某个节点出发按照特定方向搜索,因此用node/income/outcome 这三个方法几乎可以组合出任意所需的查询 。
但对于复杂查询,写出的代码有时会显得较为琐碎和冗长 , 对于特定领域来说,往往存在更为简洁的名称,例如:母亲的兄弟可简称为舅舅 。对于这些场景 , 如果能够类似DSL (领域特定语言)那样允许用户根据专业要求自行扩展,从而简化查询,方便阅读 , 无疑会更为友好 。
如果读者去看原作者的实现,会发现他是用一种特殊语法addAlias 来定义自己想要的查询 , 调用方法时再进行查询以确定要执行的内容,其接口和内部实现都是相当复杂的 。
而我希望有更简单的方法来实现这一点 。所幸Python 是一种高度动态的语言,允许在运行时向类中增加新的成员,因此做到这一点可能比预想的还要简单 。
为了验证这一点,编写测试如下:
无需Dagoba 的实现做任何改动,测试就可以通过了!其实我们要做的就是动态添加一个自定义的成员函数,按照 Python 对象机制的要求,成员函数的第一个成员应该是名为 self 的参数 , 但这里已经是在 UnitTest 的内部,为了和测试类本身的 self 相区分,新函数的参数增加了一个下划线 。
此外,函数应返回其所属的对象,这是为了链式调用所要求的 。我们看到 , 动态语言的灵活性使得添加新语法变得非常简单 。
到此,一个初具规模的图数据库就形成了 。
和原文相比,本文还缺少一些内容,比如如何将数据库序列化到磁盘 。不过相信读者都看到了 , 我们的数据库内部结构基本上是简单的原生数据结构(列表+字典),因此序列化无论用pickle 或是 JSON 之类方法都应该是相当简单的 。有兴趣的读者可以自行完成它们 。
推荐阅读
- 看微信直播骗术,看微信直播骗术揭秘
- 还需不需要学jquery,学了jquery有必要学vue吗
- h3c路由器怎么查看负载,h3c查看端口负载
- 小学生三年级益智游戏大全,适合小学三年级玩的益智游戏
- vb.net的数据类型 vb6数据类型
- linux命令行有空格,命令行路径有空格
- 损坏酒店电视怎么处理,弄坏酒店电视机怎么办
- 红运直播录屏怎么录视频,红运直播录屏怎么录视频教程
- python数学函数库 python math函数库