Похоже, что вы изменили FakeModule
между моментом, когда вы извлекали свои данные, и временем, когда вы пытались их распаковать: в частности, вы удалили из этого модуля некоторый объект верхнего уровня с именем World
(возможно,класс, возможно, функция).
Pickling сериализует классы и функции «по имени», поэтому они должны быть именами на верхнем уровне своего модуля и , чтобы модуль не мог быть изменен (припо крайней мере, не таким образом, чтобы повлиять на эти имена плохо - определенно не на удаление этих имен из модуля!) между временем посадки и вскрытия.
После того, как выВы точно определили, какие изменения вы сделали, что препятствует снятию травления, часто их можно взломать, если по другим причинам вы не можете просто отменить изменение.Например, если вы только что переместили World
с FakeModule
на CoolModule
, выполните:
import FakeModule
import CoolModule
FakeModule.World = CoolModule.World
непосредственно перед снятием травления (и не забудьте снова засолить с новой структурой, чтобы вы неприходится повторять эти хаки каждый раз, когда вы отсеиваете; -).
Edit : редактирование Q в OP делает его ошибку намного легче понять.Поскольку он теперь проверяет, равен ли __name__
'__main__'
, становится очевидным, что при написании pickle будет сохранять объект класса __main__.World
.Так как он использует ASCII-метки (кстати, очень плохой выбор для производительности и дискового пространства), тривиально проверить:
$ cat file
(i__main__
World
p0
(dp1
модуль, который ищется (ясно иочевидно) __main__
.Теперь, даже не беспокоясь о ipython, но с помощью простого интерактивного интерпретатора Python:
$ py26
Python 2.6.5 (r265:79359, Mar 24 2010, 01:32:55)
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import world
>>> import pickle
>>> pickle.load(open("file", "rb"))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py", line 1370, in load
return Unpickler(file).load()
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py", line 858, in load
dispatch[key](self)
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py", line 1069, in load_inst
klass = self.find_class(module, name)
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py", line 1126, in find_class
klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'World'
>>>
ошибка может быть легко воспроизведена, и ее причина столь же очевидна: модуль, в котором выполняется поиск имени класса (то есть__main__
) действительно не имеет атрибута с именем "World".В модуле world
он есть, но OP не «соединил точки», как я объяснил в предыдущей части ответа, поместив ссылку с правильным именем в модуль, в котором это требуется для маринованного файла.То есть:
>>> World = world.World
>>> pickle.load(open("file", "rb"))
<world.World instance at 0xf5300>
>>>
теперь это работает просто отлично, конечно (и, как я уже говорил ранее).Возможно, ОП не видит этой проблемы, потому что он использует форму импорта, которую я ненавижу, from world import World
(импорт функции или класса непосредственно из модуля, а не самого модуля).
Взлом на работупроблема в ipython точно такая же с точки зрения базовой архитектуры Python - просто требуется еще пара строк кода, потому что ipython для предоставления всех своих дополнительных сервисов не напрямую создает модуль __main__
доступен для прямой записи того, что происходит в интерактивной командной строке, но вместо этого вставляет одну (называемую FakeModule, как OP обнаружил по ошибке msg ;-) и выполняет с ней черную магию, чтобы быть «крутым» и т. д.Тем не менее, всякий раз, когда вы хотите напрямую перейти к модулю с заданным именем, в Python это, конечно, тривиально:
In [1]: import world
In [2]: import pickle
In [3]: import sys
In [4]: sys.modules['__main__'].World = world.World
In [5]: pickle.load(open("file", "rb"))
Out[5]: <world.World instance at 0x118fc10>
In [6]:
Урок, который нужно сохранить, номер один: избегайте черной магии, по крайней мере до тех пор, покавы достаточно хороши как ученик колдуна, чтобы иметь возможность обнаруживать и исправлять его случайные побеги (в противном случае эти метлы с ведрами могут затопить мир, пока вы спите; -).
Или, альтернативаЧтение: чтобы правильно использовать определенный уровень абстракции (например, «крутые», которые ipython помещает поверх Python), вам необходимо глубокое понимание нижележащего уровня (здесь, самого Python и его основных механизмов, таких как pickling и sys.modules).
Урок номер два: этот файл выбора, по сути, поврежден из-за того, как вы его написали, потому что он может быть загружен только тогда, когда модуль __main__
имеет класс по имени Word
, который изКонечно, как правило, не будет без некоторых хаков, как указано выше.Вместо этого файл pickle должен записывать класс как находящийся в модуле world
.Если вы абсолютно уверены, что должны создать файл по предложению if __name__ == '__main__':
в world.py
, тогда используйте некоторую избыточность для этой цели:
import pickle
class World:
""
if __name__ == '__main__':
import world
w = world.World()
pickle.dump(w, open("file", "wb"))
это работает нормально и без взломов (по крайней мере, если вы будете следовать рекомендациям Python о том, чтобы никогда не иметь какого-либо существенного кода на верхнем уровне модуля - только импорт, класс, def и тривиальные назначения - все остальное принадлежит функциям; еслиВы не следовали этой наилучшей практике, а затем отредактируйте свой код, чтобы сделать вас намного счастливее с точки зрения как гибкости, так и производительности).