Невозможно рассортировать 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_)