2016-03-02 62 views
6

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 。不过,我在这里留下这个问题是为了学术上的兴趣。

+0

传递给'defaultdict'的方法必须创建一个新实例,而不是引用回现有对象。在你的情况下,'tree'是一个返回一个新的'defaultdict(tree)'的方法,但是你的一行是一个lambda,它返回'defaultdict(lambda:等)的原始'self._tree'实例' – PaulMcG

+0

@ PaulMcGuire:那么为什么'd = collections.defaultdict(lambda:d); d ['foo'] ['bar']'尽管在打印时很丑陋,似乎可以正常工作? – Sardathrion

+0

Kevin的回答和Tadhg McDonald-Jensen的评论说明了单线lambda方法中的不当递归。 – PaulMcG

回答

5

第一个链接问题中的单行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... 

在你的第二个链接的单行拉姆达实现的功能看起来更像是:

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


编辑:看起来你可以在一个声明中做到:

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

......但要求大学水平的lambda微积分技能只是为了让你的脚本缩小一个语句似乎是不明智的讨价还价。考虑一个更容易理解的方法。

+0

在将值设置为键“b”之前,这很容易通过'assert d is d ['b']'进行验证。它只是指向自己。 –

+0

为了抢先任何评论,问“好吧,但你可以在一行没有分号吗?”我认为这在理论上是可行的,但它需要lambda的一些相当棘手的用法。可能需要定点组合器。我仍然在玩它。 – Kevin

+1

@凯文:谢谢!当然,一个人不应该[首先分配​​lambda](http://legacy.python.org/dev/peps/pep-0008/#programming-recommendations)。 – Sardathrion

2

即使从that answer是具有完全相同的问题代码:

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

每个子字典只给自己创造一个参考,而不是一个新的字典。

为了使其正常工作,lambda需要创建一个新的defaultdict对象(lambda表达式)作为第一个参数。然而到拉姆达的唯一参考保持为self._tree.default_factory这样的一行将不得不像这样:

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

这使本来混乱,我不能强调不够,我是多么建议做在一行中。

+1

100%同意你的最终陈述,编辑这个问题让未来的读者清楚。 – Sardathrion