8

基于这个答案,我想创建一个单行树作为另一个类的一部分,如下所示:

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 表达式?


Python之禅

简单胜于复杂。

单行 lambda 使代码复杂,而实现更简单因此,不应在生产代码中使用单行 lambda 。但是,出于学术兴趣,我将把这个问题留在这里。

4

2 回答 2

7

第一个链接问题中的单行 defaultdict 设计对我来说看起来不正确。它会产生不寻常的自引用循环:

>>> 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 演算技能只是通过一个语句来缩小你的脚本似乎是不明智的讨价还价。考虑一种更容易理解的方法。

于 2016-03-02T16:12:24.897 回答
2

即使使用该答案中的代码,它也存在完全相同的问题:

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

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

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

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

这本质上令人困惑,以至于我不能足够强调我建议不要在一行中这样做。

于 2016-03-02T17:47:59.697 回答