一行树实现

时间:2016-03-02 16:01:19

标签: python dictionary data-structures autovivification

Based of this answer,我想创建一个one line tree作为另一个类的一部分,如下所示:

self._tree = collections.defaultdict(lambda: self._tree)

我需要允许所述类的用户将路径元素添加到树中,并从最低的树级开始运行一些回调。当我运行pytest

时,我的天真实现会引发错误
def _add(self, tree, path):
    for node in path:
        tree = tree[node]

def _run(self, tree, callback):
    for key in tree.keys():
        callback(tree[key]) # !!! Recursion detected (same locals & position)
        self._run(key)

如果树定义为

,则此代码有效
    def tree():
        return collections.defaultdict(tree)

    self._tree = tree()

为什么我的天真方法不能使用lambda表达式?

Zen of Python表示

  

简单比复杂更好。

单行lambda使代码复杂,其中有simpler implementation。因此,单行lambda 不应在生产代码中使用。但是,为了学术兴趣,我将把这个问题留在这里。

2 个答案:

答案 0 :(得分:6)

第一个链接问题中的单行默认设计对我来说不合适。它产生了不寻常的自引用循环:

>>> d = collections.defaultdict(lambda: d)
>>> d["a"] = 23
>>> d["b"]["c"] = 42
>>> print d["b"]["a"] #we never created a value with these keys, so it should just return a defaultdict instance.
23
>>> #uh, that's not right...

第二个链接中该函数的单行lambda实现看起来更像:

tree = lambda: defaultdict(tree); self._tree = tree()

编辑:看起来你可以在一个语句中使用:

self._tree = (lambda f: f(f))(lambda t: defaultdict(lambda: t(t)))

...但是要求大学级别的lambda演算技巧只是为了缩小你的剧本一个陈述似乎是一个不明智的讨价还价。考虑一种更容易理解的方法。

答案 1 :(得分:2)

即使使用that answer中的代码,它也会遇到完全相同的问题:

d = collections.defaultdict(lambda:d)
assert d is d[1] is d[2][4]

每个子dict只创建一个对自身的引用而不是新的字典。

为了使其正常工作,lambda需要创建一个新的defaultdict对象,其自身(lambda表达式)作为第一个参数。然而,对lambda的唯一引用保持为self._tree.default_factory,因此单行必须看起来像这样:

self._tree = collections.defaultdict(lambda:collections.defaultdict(self._tree.default_factory))

这本质上是令人困惑的,我无法强调我建议反对在一行中做多少。