python模型管道函数 python管道阻塞( 七 )


在此步骤的最后,我们再实现一个优化 。当多次调用查询方法后 , 结果可能会返回重复的数据,很多时候这是不必要的 。就像关系数据库通常支持unique/distinct 一样,我们也希望 Dagoba 能够过滤重复的数据 。
假设我们要查询某人所有孩子的祖父 , 显然不管有多少孩子,他们的祖父应该是同一个人 。因此编写测试如下:
现在来实现unique。我们只要按照主键把重复数据去掉即可:
在上个步骤,初始化数据库指定了双向关联,但并未测试它们 。因为我们还没有编写代码去支持它们,现在增加一个测试,它应该是失败的:
运行测试,的确失败了 。我们看看要如何支持它 。回想一下 , 当从边查找节点时 , 使用的是以下方法:
这里也有一个潜在的问题:调用self.edges 意味着遍历所有边,当数据库内容较多时,这是巨大的浪费 。为了提高性能,我们可以把与节点相关的边记录在节点本身,这样要查找边只要看节点本身即可 。在初始化时定义出入边的集合:
在添加边时,我们要同时把它们对应的关系同时更新到节点,此外还要维护反向关联 。这涉及对字典内容的部分复制,先编写一个辅助方法:
然后,将添加边的实现修改如下:
这里的代码同时添加正向关联和反向关联 。有的朋友可能会注意到代码略有重复,是的,但是重复仅出现在该函数内部 , 本着“三则重构”的原则,暂时不去提取代码 。
实现之后,前面的测试就可以正常通过了 。
在这个步骤中,我们来实现延迟查询( Lazy Query ) 。
延迟查询的要求是,当调用查询方法时并不立即执行,而是推迟到调用特定方法,比如run 时才执行整个查询,返回结果 。
延迟查询的实现要比主动查询复杂一些 。为了实现延迟查询,查询方法的实现不能直接返回结果,而是记录要执行的动作以及传入的参数,到调用run 时再依次执行前面记录下来的内容 。
如果你去看作者的实现 , 会发现他是用一个数据结构记录执行操作和参数,此外还有一部分逻辑用来分派对每种结构要执行的动作 。这样当然是可行的,但数据处理和分派部分的实现会比较复杂,也容易出错 。
本文的实现则选择了另外一种不同的方法:使用Python 的内部函数机制,把一连串查询变换成一组函数 , 每个函数取上个函数的执行结果作为输入,最后一个函数的输出就是整个查询的结果 。由于内部函数同时也是闭包 , 尽管每个查询的参数形式各不相同,但是它们都可以被闭包“捕获”而成为内部变量,所以这些内部函数可以采用统一的形式,无需再针对每种查询设计额外的数据结构 , 因而执行过程得到了很大程度的简化 。
首先还是来编写测试 。LazyQueryTest 和 EagerQueryTest 测试用例几乎是完全相同的(是的 , 两种查询只在于内部实现机制不同,它们的调用接口几乎是完全一致的) 。
因此我们可以把EagerQueryTest 的测试原样不变拷贝到 LazyQueryTest 中 。当然拷贝粘贴不是个好注意 , 对于比较冗长而固定的初始化部分,我们可以把它提取出来作为两个测试共享的公共函数 。读者可参考代码中的 step04_lazy_query/tests/test_lazy_query.py 部分 。
程序把查询函数的串行执行称为管道( pipeline ),用一个变量来记录它:
然后依次实现各个调用接口 。每种接口的实现都是类似的:用内部函数执行真正的查询逻辑,再把这个函数添加到pipeline 调用链中 。比如 node 的实现类似下面:

推荐阅读