не понимаю это лямбда-выражение с 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 ответов
это довольно умный способ создать рекурсивный defaultdict
. Сначала это немного сложно понять, но как только вы копнете в то, что происходит, это на самом деле довольно простое использование рекурсии.
в этом примере мы определяем функцию рекурсивную лямбда, tree
, который возвращает defaultdict
конструктор которого 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>, {})})
после перезаписи значения по умолчанию ключом вы больше не можете создавать произвольно глубокие вложенные словари.
recursive_dict['bob']['age']['year'] = 2016
# TypeError: 'int' object does not support item assignment
надеюсь, это все прояснит!
два замечания:
-
lambda
представляет собой анонимную функцию. - функции являются первоклассными объектами в Python. Они могут быть назначены переменной, как и любой другой объект.
Итак, вот 2 разных способа определения функционально идентичных объектов. Это рекурсивные функции, потому что они ссылаются на себя.
from collections import defaultdict
# anonymous
tree = lambda: defaultdict(tree)
# explicit
def tree(): return defaultdict(tree)
запуск последних 2 строк с этими различными определениями в свою очередь, вы видите только тонкий разница в названии 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'})})
легче увидеть, если вы попробуете это:a = lambda: a
, вы увидите, что a()
возвращает a
. Так...
>>> a = lambda: a
>>> a()()()()
<function <lambda> at 0x102bffd08>
они делают это с помощью тега defaultdict
тоже. tree
- это функция, возвращающая defaultdict, значение по умолчанию которой является еще одним defaultdict, и так далее.
я тоже об этом не знал. Я думал tree
сначала нужно было бы определить. Может быть, это специальное правило Python? (EDIT:) Нет, я забыл, что Python выполняет поиск имени во время выполнения, и tree
уже указывает на лямбду. В 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