用defaultdict不理解这个lambda表达式

时间:2018-08-13 18:36:58

标签: python python-3.x lambda defaultdict

我在pythontips上看到了这个示例。当defaultdict接受参数“ tree”并返回“ tree”时,我不明白第二行。

import collections
tree = lambda: collections.defaultdict(tree)
some_dict = tree()
some_dict['color']['favor'] = "yellow"
# Works fine

运行这段代码后,我检查了some_dict的类型

defaultdict(< function < lambda > at 0x7f19ae634048 >, 
            {'color': defaultdict(
                  < function < lambda > at 0x7f19ae634048 >, {'favor': 'yellow'})})

3 个答案:

答案 0 :(得分:3)

注意两点:

  1. lambda代表一个匿名函数。
  2. 函数是Python中的一流对象。它们可以像其他任何对象一样分配给变量。

因此,这里有2种不同的方法来定义功能相同的对象。它们是递归函数,因为它们引用了自己。

from collections import defaultdict

# anonymous
tree = lambda: defaultdict(tree)

# explicit
def tree(): return defaultdict(tree)

依次使用这些不同的定义运行最后两行,您会发现defaultdict类型的命名只有细微的差别:

# anonymous
defaultdict(<function __main__.<lambda>()>,
            {'color': defaultdict(<function __main__.<lambda>()>,
                         {'favor': 'yellow'})})

# explicit
defaultdict(<function __main__.tree()>,
            {'color': defaultdict(<function __main__.tree()>,
                         {'favor': 'yellow'})})

答案 1 :(得分:1)

看是否尝试这样做更容易:a = lambda: a,您会看到a()返回a。所以...

>>> a = lambda: a
>>> a()()()()
<function <lambda> at 0x102bffd08>

他们也使用defaultdict进行此操作。 tree是一个返回defaultdict的函数,其默认值为另一个defaultdict,依此类推。

我实际上也没有意识到这一点。我认为tree必须先定义。也许这是一条特殊的Python规则? (编辑:)不,我忘记了Python在运行时进行名称查找,然后tree便指向了lambda。在C ++中,有编译时引用检查,但是您可以定义引用自己的函数。

这似乎是一种创建某些用户无法预期的行为的方法。就像说您不小心在以后重新定义tree一样,您的defaultdict被破坏了:

>>> import collections
>>> tree = lambda: collections.defaultdict(tree)
>>> some_dict = tree()
>>> tree = 4
>>> some_dict[4][3] = 2 # TypeError: first argument must be callable or None

答案 2 :(得分:1)

这是创建递归defaultdict的非常聪明的方法。刚开始理解时会有些棘手,但是一旦您深入了解正在发生的事情,实际上就是一种非常简单的递归用法。

在此示例中,我们定义了一个递归lambda函数tree,该函数返回其构造函数为defaultdict的{​​{1}}。为了清楚起见,让我们使用常规函数重写它。

tree

请注意,我们返回的是from collections import defaultdict from pprint import pprint def get_recursive_dict(): return defaultdict(get_recursive_dict) ,而不是defaultdict(get_recursive_dict)。我们要传递defaultdict(get_recursive_dict())的可调用对象(即函数defaultdict)。实际上,调用get_recursive_dict将导致无限递归。

如果调用get_recursive_dict(),则会得到一个空get_recursive_dict,其默认值为函数defaultdict

get_recursive_dict

让我们看看这一点。创建密钥recursive_dict = get_recursive_dict() print(recursive_dict) # defaultdict(<function get_recursive_dict at 0x0000000004FFC4A8>, {}) ,它的对应值默认为空的'alice',其默认值为函数defaultdict。注意,这是与我们的get_recursive_dict相同的默认值!

recursive_dict

因此,我们可以创建任意数量的嵌套字典。

print(recursive_dict['alice'])
# defaultdict(<function get_recursive_dict at 0x0000000004AF46D8>, {})
print(recursive_dict)
# defaultdict(<function get_recursive_dict at 0x0000000004AF46D8>, {'alice': defaultdict(<function get_recursive_dict at 0x0000000004AF46D8>, {})})

一旦用键覆盖默认值,就无法再创建任意深度嵌套的字典。

recursive_dict['bob']['age'] = 2
recursive_dict['charlie']['food']['dessert'] = 'cake'
print(recursive_dict)
# defaultdict(<function get_recursive_dict at 0x00000000049BD4A8>, {'charlie': defaultdict(<function get_recursive_dict at 0x00000000049BD4A8>, {'food': defaultdict(<function get_recursive_dict at 0x00000000049BD4A8>, {'dessert': 'cake'})}), 'bob': defaultdict(<function get_recursive_dict at 0x00000000049BD4A8>, {'age': 2}), 'alice': defaultdict(<function get_recursive_dict at 0x00000000049BD4A8>, {})})

我希望这可以解决所有问题!