Как преобразовать defaultdict defaultdicts [defaultdicts] в dict dicts [of dicts]?
Используя этот ответ, я создал defaultdict
defaultdict
s. Теперь я хотел бы превратить этот глубоко вложенный объект dict в обычный питон-питон.
from collections import defaultdict
factory = lambda: defaultdict(factory)
defdict = factory()
defdict['one']['two']['three']['four'] = 5
# defaultdict(<function <lambda> at 0x10886f0c8>, {
# 'one': defaultdict(<function <lambda> at 0x10886f0c8>, {
# 'two': defaultdict(<function <lambda> at 0x10886f0c8>, {
# 'three': defaultdict(<function <lambda> at 0x10886f0c8>, {
# 'four': 5})})})})
Я предполагаю, что это неправильное решение:
import json
regdict = json.loads(json.dumps(defdict))
# {u'one': {u'two': {u'three': {u'four': 5}}}}
Кроме того, этот ответ неадекватен, поскольку он не рекурсирует по вложенному dict (s).
Ответы
Ответ 1
Вы можете переписывать дерево, заменяя каждый экземпляр defaultdict
на dict, defaultdict
dict:
def default_to_regular(d):
if isinstance(d, defaultdict):
d = {k: default_to_regular(v) for k, v in d.items()}
return d
Демо-версия:
>>> from collections import defaultdict
>>> factory = lambda: defaultdict(factory)
>>> defdict = factory()
>>> defdict['one']['two']['three']['four'] = 5
>>> defdict
defaultdict(<function <lambda> at 0x103098ed8>, {'one': defaultdict(<function <lambda> at 0x103098ed8>, {'two': defaultdict(<function <lambda> at 0x103098ed8>, {'three': defaultdict(<function <lambda> at 0x103098ed8>, {'four': 5})})})})
>>> default_to_regular(defdict)
{'one': {'two': {'three': {'four': 5}}}}
Ответ 2
То, что вы на самом деле пытаетесь сделать, - это defaultdict
ваш рекурсивный defaultdict
. И вам все равно, верните ли вы dict
или defaultdict
при рассыпании.
Хотя существует несколько способов решить эту проблему (например, создать подклассу defaultdict
с собственным травлением или явно переопределить значение по умолчанию с помощью copyreg
), там есть один мертвый тривиальный.
Обратите внимание на ошибку, которую вы получаете при попытке:
>>> pickle.dumps(defdict)
PicklingError: Can't pickle <function <lambda> at 0x10d7f4c80>: attribute lookup <lambda> on __main__ failed
Вы не можете расчехлять функции lambda
-defined, потому что они анонимны, что означает, что они никогда не могут быть разбросаны.
Но буквально нет причин, чтобы эта функция определялась lambda
. В частности, вы даже не хотите, чтобы это было анонимно, потому что вы явно даете ему имя. Так:
def factory(): return defaultdict(factory)
И вы сделали.
Здесь он находится в действии:
>>> from collections import defaultdict
>>> def factory(): return defaultdict(factory)
>>> defdict = factory()
>>> defdict['one']['two']['three']['four'] = 5
>>> import pickle
>>> pickle.dumps(defdict)
b'\x80\x03ccollections\ndefaultdict\nq\x00c__main__\nfactory\nq\x01\x85q\x02Rq\x03X\x03\x00\x00\x00oneq\x04h\x00h\x01\x85q\x05Rq\x06X\x03\x00\x00\x00twoq\x07h\x00h\x01\x85q\x08Rq\tX\x05\x00\x00\x00threeq\nh\x00h\x01\x85q\x0bRq\x0cX\x04\x00\x00\x00fourq\rK\x05ssss.'
Существуют и другие случаи, когда использование lambda
вместо def
без уважительной причины вызовет проблемы - вы не можете также инспектировать свои функции во время выполнения, вы ухудшаете трассировку в отладчике и т.д. Используйте lambda
когда вы хотите использовать анонимную функцию, или функцию, которую вы можете определить в середине выражения, но не используйте ее для сохранения трех символов ввода.
Ответ 3
Одна из возможностей заключается в том, что вы можете создать свой собственный класс, который можно переключить с режима "defaultdict" в "ванильный" режим. Для больших словарей это было бы намного, намного быстрее. Речь идет о переопределении метода getitem и наличии соответствующей переменной экземпляра для управления поведением.