UnpicklingError: Аргумент класса NEWOBJ не является типом объекта
Я использую пользовательский инструмент выбора, который заменяет любые непереборчивые объекты (например, сокеты или файлы) их строковым представлением, основанным на коде из Шейна Хэтэуэй: Python: выбор диска с некоторыми непопулярными элементами
Это работает большую часть времени, но когда я пытаюсь распаковать Django HttpResponse, я получаю следующую ошибку: UnpicklingError: Аргумент класса NEWOBJ не является объектом типа
Я понятия не имею, что на самом деле означает ошибка. Если он маринован, хорошо, почему он не должен быть в состоянии мариновать? Я нашел три ссылки на эту ошибку в Google, но нет реального объяснения, почему она возникает или как ее исправить.
Вот мой код:
from cPickle import Pickler, Unpickler, UnpicklingError
class FilteredObject:
def __init__(self, about):
self.about = about
def __repr__(self):
return 'FilteredObject(%s)' % repr(self.about)
class MyPickler(object):
def __init__(self, file, protocol=2):
pickler = Pickler(file, protocol)
pickler.persistent_id = self.persistent_id
self.dump = pickler.dump
self.clear_memo = pickler.clear_memo
def persistent_id(self, obj):
if not hasattr(obj, '__getstate__') and not isinstance(obj,
(basestring, bool, int, long, float, complex, tuple, list, set, dict)):
return ["filtered:%s" % str(obj)]
else:
return None
class MyUnpickler(object):
def __init__(self, file):
unpickler = Unpickler(file)
unpickler.persistent_load = self.persistent_load
self.load = unpickler.load
self.noload = unpickler.noload
def persistent_load(self, obj_id):
if obj_id[0].startswith('filtered:'):
return FilteredObject(obj_id[0][9:])
else:
raise UnpicklingError('Invalid persistent id')
###### serialize to file
f = open('test.txt','wb')
p = MyPickler(f)
p.dump(data)
f.close()
###### unserialize from file
f = open('test.txt','rb')
pickled_data = f.read()
f.seek(0)
u = MyUnpickler(f)
data = u.load()
1 ответ
Успешное травление происходит в два этапа: pickle.dump от Pickler и pickle.load от Unpickler. Средство Pickler преобразует объект в сериализованный формат (например, строки), а Unpickler переваривает засоленный объект и генерирует новый объект, который должен быть эквивалентен оригиналу. У Pickle есть несколько функций, которые можно использовать для сброса Pickle... так что часть 1 заставляет объекты преобразовываться в сериализованный формат. С помощью специального средства выбора вы можете обойти некоторые меры безопасности Python для выбора объектов, которые pickle
сама не может мариновать. Следуя вашему примеру, я мог бы создать простой Pickler, который конвертировал бы лямбды и еще много чего в строки путем преобразования каждого объекта в его __repr__
,
>>> x = lambda x:x
>>> repr(x)
'<function <lambda> at 0x4d39cf0>'
>>>
>>> import pickle
>>> l = repr(x)
>>> pickle.dumps(l)
"S'<function <lambda> at 0x4d39cf0>'\np0\n."
Это определенно будет маринованным, так как это струна. Однако проблема заключается в том, как построить объект из сохраненной строки. Для лямбды, если бы у вас была функция, которая могла бы искать ссылку на память, отмеченную в строке, вы могли бы вернуть объект... но только если у вас все еще был живой исходный объект в вашей памяти... так что нет хорошо. Таким образом, хитрость преобразования в строки работает только тогда, когда в __repr__
строка для создания нового объекта из сохраненной строки. Вы можете узнать больше о том, что вы храните, но в конечном итоге вы столкнетесь с проблемами, скорее всего, преобразовав объекты в строки. Так что это тот случай, когда ваш Pickler будет работать, но ваш Unpickler потерпит неудачу.
Словари интересны для маринования, потому что в них может быть что угодно, и обычно это делается довольно быстро. Одним из самых противных словарей является globals()
толковый словарь. Чтобы сериализовать его, я бы использовал укроп, который может сериализовать практически все в Python.
>>> import dill
>>>
>>> def foo(a):
... def bar(x):
... return a*x
... return bar
...
>>> class baz(object):
... def __call__(self, a,x):
... return foo(a)(x)
...
>>> b = baz()
>>> b(3,2)
6
>>> c = baz.__call__
>>> c(b,3,2)
6
>>> g = dill.loads(dill.dumps(globals()))
>>> g
{'dill': <module 'dill' from '/Library/Frameworks/Python.framework/Versions/7.2/lib/python2.7/site-packages/dill-0.2a.dev-py2.7.egg/dill/__init__.pyc'>, 'c': <unbound method baz.__call__>, 'b': <__main__.baz object at 0x4d61970>, 'g': {...}, '__builtins__': <module '__builtin__' (built-in)>, 'baz': <class '__main__.baz'>, '_version': '2', '__package__': None, '__name__': '__main__', 'foo': <function foo at 0x4d39d30>, '__doc__': None}
На самом деле, укроп регистрирует свои виды в pickle
реестра, так что если у вас есть код черного ящика, который использует pickle
и вы не можете его отредактировать, тогда просто импортирование укропа может волшебным образом заставить его работать без мартовских патчей стороннего кода.
Или, если вы хотите, чтобы весь сеанс переводчика передавался как "образ питона", укроп тоже может это сделать.
>>> # continuing from above
>>> dill.dump_session('foobar.pkl')
>>>
>>> ^D
dude@sakurai>$ python
Python 2.7.5 (default, Sep 30 2013, 20:15:49)
[GCC 4.2.1 (Apple Inc. build 5566)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('foobar.pkl')
>>> c(b,3,2)
6
У Дилла также есть несколько хороших инструментов, которые помогут вам понять, что приводит к сбою процесса перебора при сбое кода.