J'ai récemment changé la disposition du répertoire de mon programme: auparavant, j'avais tous mes modules dans le dossier "principal". Maintenant, je les ai déplacés dans un répertoire nommé d'après le programme et placé un __init__.py
là pour faire un paquet.
Maintenant, j'ai un seul fichier .py dans mon répertoire principal qui est utilisé pour lancer mon programme, ce qui est beaucoup plus soigné.
Quoi qu'il en soit, essayer de charger des fichiers marinés à partir de versions précédentes de mon programme échoue. Je reçois "ImportError: Aucun module nommé tools" - ce qui, je suppose, est dû au fait que mon module était auparavant dans le dossier principal, et maintenant il se trouve dans whyteboard.tools, et pas simplement des outils simples. Cependant, le code qui est importé dans le module tools vit dans le même répertoire que lui, donc je doute qu'il soit nécessaire de spécifier un package.
Donc, mon répertoire de programme ressemble à ceci:
whyteboard-0.39.4
-->whyteboard.py
-->README.txt
-->CHANGELOG.txt
---->whyteboard/
---->whyteboard/__init__.py
---->whyteboard/gui.py
---->whyteboard/tools.py
whyteboard.py lance un bloc de code à partir de whyteboard/gui.py, qui déclenche l'interface graphique. Ce problème de décapage ne se produisait certainement pas avant la réorganisation du répertoire.
Comme documents de pickle disons, afin de sauvegarder et restaurer une instance de classe (en fait une fonction aussi), vous devez respecter certaines contraintes:
pickle peut enregistrer et restaurer des instances de classe de manière transparente, mais la définition de classe doit être importable et vivre dans le même module que lorsque l'objet a été stocké
whyteboard.tools
n'est pas le "même module que" tools
(même s'il peut être importé par import tools
par d'autres modules du même package, il se retrouve dans sys.modules
comme sys.modules['whyteboard.tools']
: c'est absolument crucial, sinon le même module importé par un dans le même package vs un dans un autre package se retrouverait avec des entrées multiples et éventuellement conflictuelles!).
Si vos fichiers pickle sont dans un format bon/avancé (par opposition à l'ancien format ascii qui est le format par défaut uniquement pour des raisons de compatibilité), leur migration une fois que vous avez effectué ces modifications peut en fait pas être tout aussi trivial que "éditer le fichier" (qui est binaire & c ...!), malgré ce qu'une autre réponse suggère. Je suggère que vous fassiez plutôt un petit "script de migration de cornichons": laissez-le patcher sys.modules
comme ça...:
import sys
from whyteboard import tools
sys.modules['tools'] = tools
puis cPickle.load
chaque fichier, del sys.modules['tools']
, et cPickle.dump
chaque objet chargé dans le fichier: cette entrée supplémentaire temporaire dans sys.modules
devrait laisser les cornichons se charger avec succès, puis les vider à nouveau devrait utiliser le bon nom de module pour les classes des instances (la suppression de cette entrée supplémentaire devrait en être sûre).
Cela m'est arrivé, résolu en ajoutant le nouvel emplacement du module à sys.path avant de charger le cornichon:
import sys
sys.path.append('path/to/whiteboard')
f = open("pickled_file", "rb")
pickle.load(f)
Cela peut être fait avec un "décompresseur" personnalisé qui utilise find_class()
:
import io
import pickle
class RenameUnpickler(pickle.Unpickler):
def find_class(self, module, name):
renamed_module = module
if module == "tools":
renamed_module = "whyteboard.tools"
return super(RenameUnpickler, self).find_class(renamed_module, name)
def renamed_load(file_obj):
return RenameUnpickler(file_obj).load()
def renamed_loads(pickled_bytes):
file_obj = io.BytesIO(pickled_bytes)
return renamed_load(file_obj)
Ensuite, vous devez utiliser renamed_load()
au lieu de pickle.load()
et renamed_loads()
au lieu de pickle.loads()
.
pickle
sérialise les classes par référence, donc si vous changez la durée de vie de la classe, elle ne sera pas décryptée car la classe ne sera pas trouvée. Si vous utilisez dill
au lieu de pickle
, vous pouvez sérialiser les classes par référence ou directement (en sérialisant directement la classe au lieu de son chemin d'importation). Vous simulez cela assez facilement en changeant simplement la définition de classe après un dump
et avant un load
.
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>>
>>> class Foo(object):
... def bar(self):
... return 5
...
>>> f = Foo()
>>>
>>> _f = dill.dumps(f)
>>>
>>> class Foo(object):
... def bar(self, x):
... return x
...
>>> g = Foo()
>>> f_ = dill.loads(_f)
>>> f_.bar()
5
>>> g.bar(4)
4
Il s'agit du comportement normal des cornichons, les objets non décapés doivent avoir leur définition du module importable .
Vous devriez pouvoir changer le chemin des modules (c'est-à-dire de tools
à whyteboard.tools
) en modifiant les fichiers décapés, car ce sont normalement de simples fichiers texte.