2009-02-20 9 views
30

Kiedy marynowane obiekt, który ma pewne cechy, które nie mogą być marynowane zakończy się niepowodzeniem z komunikatem o błędzie rodzajowe jak:Jak określić, który z atrybutów obiektu nie działa?

PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed 

Czy istnieje jakiś sposób, aby powiedzieć, który atrybut spowodował wyjątek? Używam Pythona 2.5.2.

Mimo że zasadniczo rozumiem podstawową przyczynę problemu (np. W powyższym przykładzie mającym metodę instancji), może być bardzo trudno dokładnie ustalić to. W moim przypadku zdefiniowałem już niestandardową metodę __getstate__, ale zapomniałem o krytycznym atrybucie. Stało się to w skomplikowanej strukturze obiektów zagnieżdżonych, więc trochę czasu zajęło mi zidentyfikowanie złego atrybutu.

Zgodnie z życzeniem, tutaj jest jeden prosty przykład celowo nie były marynatę:

import cPickle as pickle 
import new 

class Test(object): 
    pass 

def test_func(self): 
    pass 

test = Test() 
pickle.dumps(test) 
print "now with instancemethod..." 
test.test_meth = new.instancemethod(test_func, test) 
pickle.dumps(test) 

To wyjście:

now with instancemethod... 
Traceback (most recent call last): 
    File "/home/wilbert/develop/workspace/Playground/src/misc/picklefail.py", line 15, in <module> 
    pickle.dumps(test) 
    File "/home/wilbert/lib/python2.5/copy_reg.py", line 69, in _reduce_ex 
    raise TypeError, "can't pickle %s objects" % base.__name__ 
TypeError: can't pickle instancemethod objects 

Niestety nie ma śladu, że atrybut test_meth przyczyną problemu.

+0

można może dać mały przykład atrybut upadającego? lub przynajmniej pokazać nieco więcej informacji zwrotnej, aby zobaczyć, w którym miejscu modułu zalewania się nie udaje? – MrTopf

+0

oh i jakiej wersji Pythona używasz? – MrTopf

+0

@MrTopf: dodał informacje – nikow

Odpowiedz

14

Można zgłosić błąd w Pythonie, ponieważ nie zawiera on bardziej pomocnych komunikatów o błędach. W międzyczasie zmodyfikuj funkcję _reduce_ex() w .

if base is self.__class__: 
    print self # new 
    raise TypeError, "can't pickle %s objects" % base.__name__ 

wyjściowa:

<bound method ?.test_func of <__main__.Test object at 0xb7f4230c>> 
Traceback (most recent call last): 
    File "nopickle.py", line 14, in ? 
    pickle.dumps(test) 
    File "/usr/lib/python2.4/copy_reg.py", line 69, in _reduce_ex 
    raise TypeError, "can't pickle %s objects" % base.__name__ 
TypeError: can't pickle instancemethod objects 
+3

dlaczego po prostu nie umieszczać "siebie" w komunikacie błędu zamiast wydruku? – MrTopf

+1

Wierzę, że obejmuje to wyjątek "TypeError" w przykładzie, jednak oryginalny wyjątek "PicklingError" nie jest adresowany. – cmcginty

+0

Funkcja TypeError powoduje błąd PicklingError. Gdy przestaniesz próbować pobierać instancemethod obiekty (lub jakikolwiek inny nieopróżnialny obiekt) wszystko powinno działać. – joeforker

8

miałem ten sam problem jak ty, ale moje zajęcia były nieco bardziej skomplikowane (czyli duże drzewo podobnych przedmiotów), więc drukowanie nie pomogło więc Zhackowałem funkcję pomocnika. Nie jest kompletny i jest przeznaczony tylko do użytku z protokołem trawiącym 2: To wystarczyło, abym mógł zlokalizować moje problemy. Jeśli chcesz go rozszerzyć, aby pokryć wszystko, protokół jest opisany pod numerem http://www.python.org/dev/peps/pep-0307/. Zmieniono ten wpis tak, aby każdy mógł zaktualizować kod.

import pickle 
def get_pickling_errors(obj,seen=None): 
    if seen == None: 
     seen = [] 
    try: 
     state = obj.__getstate__() 
    except AttributeError: 
     return 
    if state == None: 
     return 
    if isinstance(state,tuple): 
     if not isinstance(state[0],dict): 
      state=state[1] 
     else: 
      state=state[0].update(state[1]) 
    result = {}  
    for i in state: 
     try: 
      pickle.dumps(state[i],protocol=2) 
     except pickle.PicklingError: 
      if not state[i] in seen: 
       seen.append(state[i]) 
       result[i]=get_pickling_errors(state[i],seen) 
    return result 

Przykładem zastosowań, gdzie K jest obiekt, który nie marynowane

>>> get_pickling_errors(K) 
{'_gen': {}, '_base': {'_gens': None}} 

Oznacza to, że nie jest attibute K._gen picklable i to samo odnosi się do K._base._gens .

+0

Dzięki, pomaga. I myślę, że '__getstate __()' powinien być zdefiniowany w odpowiedniej klasie. –

3

I odkryli, że jeśli podklasy Pickler i zawinąć metodę Pickler.save() w próbie, z wyjątkiem bloku

import pickle 
class MyPickler (pickle.Pickler): 
    def save(self, obj): 
     try: 
      pickle.Pickler.save(self, obj) 
     except Exception, e: 
      import pdb;pdb.set_trace() 

Następnie nazwać jak tak

import StringIO 
output = StringIO.StringIO() 
MyPickler(output).dump(thingee) 
+0

Czy to nie tylko uruchomienie debuggera? Czy jest jakaś różnica, na przykład, po prostu używając Eclipse PyDev Debugger, aby przyjrzeć się problemowi? – nikow

+0

Jeśli debugger PyDev uruchomi się na wyjątku i postawi cię na właściwym poziomie wykonania, jest taki sam. Nie używam jednak IDE. –

+0

Użyłem tego do zlokalizowania szczególnie trudnego do znalezienia błędu marynowania. Ale zamiast mieć klauzulę except uruchamiać debugger, po prostu miałem to wydruk obj i podniosłem wyjątek. Następnie uruchamianie MyPickler (output) .dump (obj) dało w rezultacie ładny raport przypominający śledzenie dokładnie, gdzie znajdował się nieprzenikliwy obiekt w moich głęboko zagnieżdżonych strukturach. Co za dzień. – partofthething

2

Jeśli używasz dill Twój przykład nie zawodzi ...

>>> import dill 
>>> import new 
>>> 
>>> class Test(object): 
...  pass 
... 
>>> def test_func(self): 
...  pass 
... 
>>> test = Test() 
>>> dill.dumps(test) 
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x04Testq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07__doc__q\x0fNutq\x10Rq\x11)\x81q\x12}q\x13b.' 
>>> test.test_meth = new.instancemethod(test_func, test) 
>>> dill.dumps(test) 
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x04Testq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07__doc__q\x0fNutq\x10Rq\x11)\x81q\x12}q\x13U\ttest_methq\x14h\x01U\nMethodTypeq\x15\x85q\x16Rq\x17cdill.dill\n_create_function\nq\x18(cdill.dill\n_unmarshal\nq\x19Ubc\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00C\x00\x00\x00s\x04\x00\x00\x00d\x00\x00S(\x01\x00\x00\x00N(\x00\x00\x00\x00(\x01\x00\x00\x00t\x04\x00\x00\x00self(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\t\x00\x00\x00test_func\x01\x00\x00\x00s\x02\x00\x00\x00\x00\x01q\x1a\x85q\x1bRq\x1cc__builtin__\n__main__\nU\ttest_funcq\x1dNN}q\x1etq\x1fRq h\x12N\x87q!Rq"sb.' 

Musimy więc znaleźć coś t kapelusz dill nie można zalać ...

>>> class Unpicklable(object): 
... def breakme(self): 
...  self.x = iter(set()) 
... 
>>> u = Unpicklable() 
>>> dill.dumps(u) 
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x0bUnpicklableq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07breakmeq\x0fcdill.dill\n_create_function\nq\x10(cdill.dill\n_unmarshal\nq\x11U\xafc\x01\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00C\x00\x00\x00s"\x00\x00\x00d\x01\x00d\x00\x00l\x00\x00}\x01\x00t\x01\x00t\x02\x00\x83\x00\x00\x83\x01\x00|\x00\x00_\x03\x00d\x00\x00S(\x02\x00\x00\x00Ni\xff\xff\xff\xff(\x04\x00\x00\x00t\t\x00\x00\x00itertoolst\x04\x00\x00\x00itert\x03\x00\x00\x00sett\x01\x00\x00\x00x(\x02\x00\x00\x00t\x04\x00\x00\x00selfR\x00\x00\x00\x00(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x07\x00\x00\x00breakme\x02\x00\x00\x00s\x04\x00\x00\x00\x00\x01\x0c\x01q\x12\x85q\x13Rq\x14c__builtin__\n__main__\nh\x0fNN}q\x15tq\x16Rq\x17U\x07__doc__q\x18Nutq\x19Rq\x1a)\x81q\x1b}q\x1cb.' 
>>> u.breakme() 
>>> dill.dumps(u) 
Traceback (most recent call last): 
…(snip)… 
pickle.PicklingError: Can't pickle <type 'setiterator'>: it's not found as __builtin__.setiterator 
>>> 

Jeśli komunikat o błędzie nie było dobre, mogę używać dill.detect aby zobaczyć, które co unpicklable obiekty przedmiotem najwyższego poziomu zawiera.

>>> dill.detect.badobjects(u, depth=1) 
{'__hash__': <method-wrapper '__hash__' of Unpicklable object at 0x10a37b350>, '__setattr__': <method-wrapper '__setattr__' of Unpicklable object at 0x10a37b350>, '__reduce_ex__': <built-in method __reduce_ex__ of Unpicklable object at 0x10a37b350>, '__reduce__': <built-in method __reduce__ of Unpicklable object at 0x10a37b350>, '__str__': <method-wrapper '__str__' of Unpicklable object at 0x10a37b350>, '__format__': <built-in method __format__ of Unpicklable object at 0x10a37b350>, '__getattribute__': <method-wrapper '__getattribute__' of Unpicklable object at 0x10a37b350>, '__delattr__': <method-wrapper '__delattr__' of Unpicklable object at 0x10a37b350>, 'breakme': <bound method Unpicklable.breakme of <__main__.Unpicklable object at 0x10a37b350>>, '__repr__': <method-wrapper '__repr__' of Unpicklable object at 0x10a37b350>, '__dict__': {'x': <setiterator object at 0x10a370820>}, 'x': <setiterator object at 0x10a370820>, '__sizeof__': <built-in method __sizeof__ of Unpicklable object at 0x10a37b350>, '__init__': <method-wrapper '__init__' of Unpicklable object at 0x10a37b350>} 
>>> dill.detect.badtypes(u, depth=1) 
{'__hash__': <type 'method-wrapper'>, '__setattr__': <type 'method-wrapper'>, '__reduce_ex__': <type 'builtin_function_or_method'>, '__reduce__': <type 'builtin_function_or_method'>, '__str__': <type 'method-wrapper'>, '__format__': <type 'builtin_function_or_method'>, '__getattribute__': <type 'method-wrapper'>, '__delattr__': <type 'method-wrapper'>, 'breakme': <type 'instancemethod'>, '__repr__': <type 'method-wrapper'>, '__dict__': <type 'dict'>, 'x': <type 'setiterator'>, '__sizeof__': <type 'builtin_function_or_method'>, '__init__': <type 'method-wrapper'>} 
>>> set(dill.detect.badtypes(u, depth=1).values()) 
set([<type 'dict'>, <type 'method-wrapper'>, <type 'instancemethod'>, <type 'setiterator'>, <type 'builtin_function_or_method'>]) 

dill nie opiera się na metodzie __getstate__ jest obecny, chociaż może i powinien wykorzystać ją, jeśli istnieje. Możesz także użyć objgraph, aby uzyskać obraz wszystkich zależności obiektów, które są używane do budowania rzeczy, która się nie podnieca. Może pomóc Ci ustalić przyczynę problemu, na podstawie powyższych informacji.

Zobacz dill.detect zastosowanie w tropieniu unpicklable przedmioty w tym numerze: https://github.com/uqfoundation/dill/issues/58

Powiązane problemy