2015-11-19 15 views
10

Perché non riesco a decapitare uno typing.NamedTuple mentre riesco a decapare un collections.namedtuple? Come posso riuscire a fare un pickle a NamedTuple?Perché non riesco a decapitare un typing.NamedTuple mentre riesco a decapitare una collection.namedtuple?

Questo codice mostra quello che ho provato finora:

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() 

uscita sul guscio:

$ 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

Questo è stato corretto in 'python 3.5.1'. –

risposta

4

E 'un bug. Ho aperto un biglietto su di esso: http://bugs.python.org/issue25665

Il problema è che namedtuple funzione durante la creazione della classe imposta il suo attributo __module__, cercando fino __name__ attributo dal globali del telaio di chiamata. In questo caso il chiamante è typing.NamedTuple.

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

Così, si finisce per impostarla come 'typing' in questo caso.

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

Fix:

Al posto di questa funzione NamedTuple dovrebbe impostarla in sé:

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 

Per ora si può anche fare:

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

Entrambe le tue correzioni funzionano, grazie mille! Inoltre, grazie per la spiegazione e per aver già inviato una patch! – marcotama

+0

Se i trigger 'AttributeError' o' ValueError', la NamedTuple risultante non sarà ancora selezionabile, corretta? Dovresti dirlo. –

Problemi correlati