Невозможно рассортировать defaultdict

У меня есть defaultdict, который выглядит так:

dict1 = defaultdict(lambda: defaultdict(int))

Проблема в том, что я не могу ее рассортировать с помощью cPickle. Одним из решений, которое я нашел здесь, является использование функции уровня модуля вместо лямбда. Мой вопрос: что такое модульная функция? Как я могу использовать словарь с cPickle?

Ответ 1

В дополнение к объяснению Martijn:

Функция уровня модуля - это функция, которая определена на уровне модуля, а это означает, что это не метод экземпляра класса, он не вложен в другую функцию и является "реальной" функцией с именем, а не лямбда-функция.

Итак, чтобы разжечь ваш defaultdict, создайте его с помощью функции уровня модуля вместо лямбда-функции:

def dd():
    return defaultdict(int)

dict1 = defaultdict(dd) # dd is a module-level function

чем вы можете рассортировать его

tmp = pickle.dumps(dict1) # no exception
new = pickle.loads(tmp)

Ответ 2

Pickle хочет сохранить все атрибуты экземпляра, а экземпляры defaultdict сохраняют ссылку на default, подлежащую вызову. Pickle рекурсирует по каждому атрибуту экземпляра.

Рассол не справляется с лямбдами; pickle только обрабатывает данные, а не код, а lambdas содержит код. Функции можно мариновать, но точно так же, как определения классов, только если функция может быть импортирована. Можно импортировать функцию, определенную на уровне модуля. Pickle просто хранит строку в этом случае, полный "путь" функции, которую нужно импортировать и ссылаться при повторной рассылке.

Ответ 3

Однако вы можете использовать partial для выполнения этого:

>>> from collections import defaultdict
>>> from functools import partial
>>> pickle.loads(pickle.dumps(defaultdict(partial(defaultdict, int))))
defaultdict(<functools.partial object at 0x94dd16c>, {})

Ответ 4

Чтобы сделать это, просто напишите код, который вы хотите написать. Я бы использовал dill, который может сериализовать lambdas и defaultdicts. Dill может сериализовать почти что угодно в python.

>>> import dill
>>> from collections import defaultdict
>>>
>>> dict1 = defaultdict(lambda: defaultdict(int))
>>> pdict1 = dill.dumps(dict1)
>>> _dict1 = dill.loads(pdict1)
>>> _dict1
defaultdict(<function <lambda> at 0x10b31b398>, {})

Ответ 5

Если вам не нужно сохранять тип defaultdict, преобразуйте его:

fname = "file.pkl"

for value in nested_default_dict:
    nested_default_dict[value] = dict(nested_default_dict[value])
my_dict = dict(nested_default_dict)

with open(fname, "wb") as f:
    pickle.dump(my_dict, f)  # Now this will work

Я думаю, что это отличная альтернатива, поскольку, когда вы травляете, объект, вероятно, находится в нем окончательной форме... И если вам действительно нужен тип defaultdict снова, вы можете просто преобразовать обратно после того, как вы распаковываете:

for value in my_dict:
    my_dict[value] = defaultdict(type, my_dict[value])
nested_default_dict = defaultdict(type, my_dict)

Ответ 6

dict1 = defaultdict(lambda: defaultdict(int))
cPickle.dump(dict(dict1), file_handle)

работал на меня

Ответ 7

В настоящее время я делаю что-то похожее на вопрос poser, однако я использую подкласс defaultdict, который имеет функцию-член, которая используется как default_factory. Чтобы мой код работал правильно (мне нужно, чтобы функция была определена во время выполнения), я просто добавил код для подготовки объекта для травления.

Вместо:

...
pickle.dump(dict, file)
...

Я использую это:

....
factory = dict.default_factory
dict.default_factory = None
pickle.dump(dict, file)
dict.default_factory = factory
...

Это не точный код, который я использовал, поскольку мое дерево - это объект, который создает экземпляры того же типа дерева, что и индексы, (поэтому я использую рекурсивную функцию-член для выполнения операций pre/post pickle), но этот шаблон также отвечает на вопрос.

Ответ 8

Реализация анонимной лямбда-функции с помощью обычной функции работала для меня. Как указал Майк, Пикл не может справиться с лямбдами; Pickle обрабатывает только данные. Поэтому, конвертируя мой метод из:

dict_ = defaultdict(lambda: default_value)

в:

default_():
  return default_value

а затем создание дикта по умолчанию для меня сработало следующим образом:

dict_ = defaultdict(default_)