2013-05-04 3 views
35

Я изучаю, как использовать рассол. Я создал объект namedtuple, добавил его в список и попытался рассортировать этот список. Тем не менее, я получаю следующее сообщение об ошибке:Как правильно сортировать экземпляр namedtuple

pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P 

Я обнаружил, что если я побежал код без окружив его внутри функции, она прекрасно работает. Есть ли дополнительный шаг, необходимый для рассортирования объекта при обертывании внутри функции?

Вот мой код:

from collections import namedtuple 
import pickle 

def pickle_test(): 
    P = namedtuple("P", "one two three four") 
    my_list = [] 
    abe = P("abraham", "lincoln", "vampire", "hunter") 
    my_list.append(abe) 
    f = open('abe.pickle', 'w') 
    pickle.dump(abe, f) 
    f.close() 

pickle_test() 
+0

К сожалению, рассол, похоже, плохо работает с именованными. – Antimony

+1

@Antimony: 'pickle' обрабатывает классы namedtuple просто отлично; классы, определенные в локальном пространстве имен функций, не так много. –

+0

Возможный дубликат [Python: невозможно сортировать тип X, поиск атрибута не удался) (http://stackoverflow.com/questions/4677012/python-cant-pickle-type-x-attribute-lookup-failed) – Air

ответ

51

Создать именованный кортеж вне функции:

from collections import namedtuple 
import pickle 

P = namedtuple("P", "one two three four") 

def pickle_test(): 
    my_list = [] 
    abe = P("abraham", "lincoln", "vampire", "hunter") 
    my_list.append(abe) 
    f = open('abe.pickle', 'w') 
    pickle.dump(abe, f) 
    f.close() 

pickle_test() 

Теперь pickle можете найти его; теперь это модуль глобальный. При распаковке все модули pickle должны найти снова __main__.P. В вашей версии P является местным, к функции pickle_test(), и это не является интроспективным или импортируемым.

Важно помнить, что namedtuple() является фабрикой класса; вы даете ему параметры и возвращает объект класса для создания экземпляров. pickle хранит только данные , содержащиеся в экземплярах, а также ссылку на исходный класс для восстановления экземпляров.

+0

Отлично! Спасибо. –

+4

Итак, что, если я создаю 'namedtuple' динамически, потому что я не знаю полей до времени выполнения? Есть ли способ обойти эту проблему? Я попытался создать другой метод вне класса, но это не сработало. – Chuim

+4

@Chuim: назначьте его глобальным модулям (используйте 'globals()' для получения сопоставления) под * одним и тем же именем *, а 'pickle' может найти его еще. –

5

После того, как я добавил свой вопрос в качестве комментария к основному ответу, я нашел способ решить проблему создания динамически созданного namedtuple разборки. Это необходимо в моем случае, потому что я обнаруживаю поля во время выполнения (после запроса БД).

Все, что я сделать, это обезьяны патчnamedtuple, эффективно перемещая его __main__ модуля:

def _CreateNamedOnMain(*args): 
    import __main__ 
    namedtupleClass = collections.namedtuple(*args) 
    setattr(__main__, namedtupleClass.__name__, namedtupleClass) 
    namedtupleClass.__module__ = "__main__" 
    return namedtupleClass 

По помнить, что namedtuple имя (которое предусмотрено args) может перезаписывать другой член в __main__ если вы не осторожны.

+11

Просто установите его на 'globals()' вместо: 'globals() [namedtupleClass .__ name__] = namedtupleClass'. Тогда нет необходимости * устанавливать '__module__'. –

+0

Когда я попробовал 'globals() [namedtupleClass .__ name__] = namedtupleClass', он действительно разрешил мне рассортировать мой объект, но когда я попытался раскрыть, у него не было' namedtupleClass', в котором он нуждался. Мой совет заключается в том, чтобы ** просто использовать словарь **, пока они не сделают рассол достаточно умным, чтобы это сделать. – Teque5

2

В качестве альтернативы, вы можете использовать cloudpickle или dill для сериализации:

from collections import namedtuple 

import cloudpickle 
import dill 



def dill_test(dynamic_names): 
    P = namedtuple('P', dynamic_names) 
    my_list = [] 
    abe = P("abraham", "lincoln", "vampire", "hunter") 
    my_list.append(abe) 
    with open('deleteme.cloudpickle', 'wb') as f: 
     cloudpickle.dump(abe, f) 
    with open('deleteme.dill', 'wb') as f: 
     dill.dump(abe, f) 


dill_test("one two three four") 
1

Я нашел this answer в другом потоке. Это все об именовании названного кортежа. Это сработало для меня:

group_t =   namedtuple('group_t', 'field1, field2') # this will work 
mismatched_group_t = namedtuple('group_t', 'field1, field2') # this will throw the error 
Смежные вопросы