2015-11-19 18 views
10

Warum kann ich nicht ein typing.NamedTuple einlegen, während ich ein collections.namedtuple beizen kann? Wie kann ich es schaffen, ein NamedTuple Pickle zu machen?Warum kann ich nicht ein typing.NamedTuple pickle, während ich ein collections.namedtuple pickle?

Dieser Code zeigt, was ich bisher versucht habe:

from collections import namedtuple 
from typing import NamedTuple 

PersonTyping = NamedTuple('PersonTyping', [('firstname',str),('lastname',str)]) 
PersonCollections = namedtuple('PersonCollections', ['firstname','lastname']) 

pt = PersonTyping("John","Smith") 
pc = PersonCollections("John","Smith") 


import pickle 
import traceback 

try: 
    with open('personTyping.pkl', 'wb') as f: 
     pickle.dump(pt, f) 
except: 
    traceback.print_exc() 
try: 
    with open('personCollections.pkl', 'wb') as f: 
     pickle.dump(pc, f) 
except: 
    traceback.print_exc() 

Ausgabe auf der Shell:

$ python3 prova.py 
Traceback (most recent call last): 
    File "prova.py", line 16, in <module> 
    pickle.dump(pt, f) 
_pickle.PicklingError: Can't pickle <class 'typing.PersonTyping'>: attribute lookup PersonTyping on typing failed 
$ 
+0

Dies wurde in 'python 3.5.1' behoben. –

Antwort

4

ein Bug Es ist. Ich habe ein Ticket auf sie eröffnet: http://bugs.python.org/issue25665

Das Problem ist, dass namedtuple Funktion, während die Klasse zu schaffen setzt seine __module__ Attribut durch __name__ Attribut von dem anrufenden Rahmens Globals aufzublicken. In diesem Fall ist der Anrufer typing.NamedTuple.

result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') 

Also, es endet es sich wie 'typing' in diesem Fall einstellen.

>>> type(pt) 
<class 'typing.PersonTyping'> # this should be __main__.PersonTyping 
>>> type(pc) 
<class '__main__.PersonCollections'> 
>>> import typing 
>>> typing.NamedTuple.__globals__['__name__'] 
'typing' 

Fix:

dieser Statt die NamedTuple Funktion sollte sie sich gesetzt:

def NamedTuple(typename, fields): 

    fields = [(n, t) for n, t in fields] 
    cls = collections.namedtuple(typename, [n for n, t in fields]) 
    cls._field_types = dict(fields) 
    try: 
     cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__') 
    except (AttributeError, ValueError): 
     pass 
    return cls 

Denn jetzt können Sie auch tun:

PersonTyping = NamedTuple('PersonTyping', [('firstname',str),('lastname',str)]) 
PersonTyping.__module__ = __name__ 
+0

Ihre beiden Fixes funktionieren, vielen Dank! Danke auch für die Erklärung und dafür, dass du schon einen Patch eingereicht hast! – marcotama

+0

Wenn 'AttributeError' oder' ValueError' ausgelöst werden, ist das resultierende NamedTuple immer noch nicht pickbar, korrekt? Du solltest das erwähnen. –