J'ai un fichier pickle qui a été créé avec python 2.7 que j'essaye de porter sur python 3.6. Le fichier est enregistré dans py 2.7 via pickle.dumps(self.saved_objects, -1)
Le portage pickle py2 à py3 chaînes deviennent des octets
et chargé en python 3.6 via loads(data, encoding="bytes")
(à partir d'un fichier ouvert en mode rb
). Si j'essaie d'ouvrir en mode r
et que je transmets encoding=latin1
à loads
, j'obtiens des erreurs UnicodeDecode. Quand je l'ouvre comme un flux d'octets, il charge, mais littéralement chaque chaîne est maintenant une chaîne d'octets. Les clés __dict__
de chaque objet sont toutes b"a_variable_name"
, ce qui génère ensuite des erreurs d'attribut lors de l'appel an_object.a_variable_name
car __getattr__
transmet une chaîne et __dict__
ne contient que des octets. J'ai l'impression d'avoir déjà essayé toutes les combinaisons d'arguments et de protocoles de pickle. En dehors de la conversion de force tous les objets __dict__
touches aux chaînes, je suis à perte. Des idées?
** Passer au 28/04/17 mise à jour pour meilleur exemple
-------------------------- -------------------------------------------------- ---------------------------------
** Mise à jour 4/27/17
Cet exemple minimal illustre mon problème:
PY 2.7.13
import pickle
class test(object):
def __init__(self):
self.x = u"test ¢" # including a unicode str breaks things
t = test()
dumpstr = pickle.dumps(t)
>>> dumpstr
"ccopy_reg\n_reconstructor\np0\n(c__main__\ntest\np1\nc__builtin__\nobject\np2\nNtp3\nRp4\n(dp5\nS'x'\np6\nVtest \xa2\np7\nsb."
PY 3.6.1
import pickle
class test(object):
def __init__(self):
self.x = "xyz"
dumpstr = b"ccopy_reg\n_reconstructor\np0\n(c__main__\ntest\np1\nc__builtin__\nobject\np2\nNtp3\nRp4\n(dp5\nS'x'\np6\nVtest \xa2\np7\nsb."
t = pickle.loads(dumpstr, encoding="bytes")
>>> t
<__main__.test object at 0x040E3DF0>
>>> t.x
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
t.x
AttributeError: 'test' object has no attribute 'x'
>>> t.__dict__
{b'x': 'test ¢'}
>>>
-------------------- -------------------------------------------------- ---------------------------------------
Mise à jour 4/28/17
Pour recréer mon problème je poste mes données cornichon brutes réelles here
Le fichier cornichon a été créé en python 2.7.13, Windows 10 à l'aide
with open("raw_data.pkl", "wb") as fileobj:
pickle.dump(library, fileobj, protocol=0)
(protocole 0 donc il est humain lisible)
Pour l'exécuter, vous aurez besoin classes.py
# classes.py
class Library(object): pass
class Book(object): pass
class Student(object): pass
class RentalDetails(object): pass
Et le script de test ici:
# load_pickle.py
import pickle, sys, itertools, os
raw_pkl = "raw_data.pkl"
is_py3 = sys.version_info.major == 3
read_modes = ["rb"]
encodings = ["bytes", "utf-8", "latin-1"]
fix_imports_choices = [True, False]
files = ["raw_data_%s.pkl" % x for x in range(3)]
def py2_test():
with open(raw_pkl, "rb") as fileobj:
loaded_object = pickle.load(fileobj)
print("library dict: %s" % (loaded_object.__dict__.keys()))
return loaded_object
def py2_dumps():
library = py2_test()
for protcol, path in enumerate(files):
print("dumping library to %s, protocol=%s" % (path, protcol))
with open(path, "wb") as writeobj:
pickle.dump(library, writeobj, protocol=protcol)
def py3_test():
# this test iterates over the different options trying to load
# the data pickled with py2 into a py3 environment
print("starting py3 test")
for (read_mode, encoding, fix_import, path) in itertools.product(read_modes, encodings, fix_imports_choices, files):
py3_load(path, read_mode=read_mode, fix_imports=fix_import, encoding=encoding)
def py3_load(path, read_mode, fix_imports, encoding):
from traceback import print_exc
print("-" * 50)
print("path=%s, read_mode = %s fix_imports = %s, encoding = %s" % (path, read_mode, fix_imports, encoding))
if not os.path.exists(path):
print("start this file with py2 first")
return
try:
with open(path, read_mode) as fileobj:
loaded_object = pickle.load(fileobj, fix_imports=fix_imports, encoding=encoding)
# print the object's __dict__
print("library dict: %s" % (loaded_object.__dict__.keys()))
# consider the test a failure if any member attributes are saved as bytes
test_passed = not any((isinstance(k, bytes) for k in loaded_object.__dict__.keys()))
print("Test %s" % ("Passed!" if test_passed else "Failed"))
except Exception:
print_exc()
print("Test Failed")
input("Press Enter to continue...")
print("-" * 50)
if is_py3:
py3_test()
else:
# py2_test()
py2_dumps()
mettre tous 3 dans le même répertoire et exécutez c:\python27\python load_pickle.py
premier qui va créer 1 fichier cornichon pour chacun des 3 protocoles. Ensuite, exécutez la même commande avec python 3 et notez que cette version convertit les clés __dict__
en octets. Je l'ai eu travailler pendant environ 6 heures, mais pour la vie de moi, je ne peux pas comprendre comment je l'ai cassé à nouveau.
Avez-vous essayé l'utf-8? – zondo
ouais, ut8, utf16, latin1 ,, cp – user2682863
Si votre pickle est dans un fichier, pourquoi utilisez-vous 'load' au lieu de' load'? – BrenBarn