2009-08-28 7 views
6

(Vous pouvez lire this question pour un arrière-plan)décapage Gracieusement dégradant en Python

Je voudrais avoir un moyen gracieusement dégradant pour décaper les objets en Python.

Lors du décapage d'un objet, appelons-le l'objet principal, parfois le Pickler déclenche une exception car il ne peut pas décaper un certain sous-objet de l'objet principal. Par exemple, une erreur que je recevais beaucoup est "ne peut pas pickle des objets de module." C'est parce que je fais référence à un module de l'objet principal. Je sais que je peux écrire un petit quelque chose pour remplacer ce module avec une façade qui contiendrait les attributs du module, mais cela aurait ses propres problèmes (1).

Donc ce que je voudrais, c'est une fonction de décapage qui remplace automatiquement les modules (et tout autre objet difficile à décaper) avec des façades qui contiennent leurs attributs. Cela peut ne pas produire un décapage parfait, mais dans de nombreux cas ce serait suffisant.

Y a-t-il quelque chose comme ça? Est-ce que quelqu'un a une idée de comment aborder cela?


(1) Un problème serait que le module puisse référencer d'autres modules de l'intérieur.

+1

Java Beans .. Python Pickles .. Je voudrais étrangler les nerds qui viennent avec cette cutesy stuff –

Répondre

0

Que diriez-vous de ce qui suit, qui est un emballage que vous pouvez utiliser pour envelopper certains modules (peut-être n'importe quel module) dans quelque chose qui est pickle-able. Vous pouvez ensuite sous-classer l'objet Pickler pour vérifier si l'objet cible est un module et, si c'est le cas, l'envelopper. Est-ce que cela accomplit ce que vous désirez?

class PickleableModuleWrapper(object): 
    def __init__(self, module): 
     # make a copy of the module's namespace in this instance 
     self.__dict__ = dict(module.__dict__) 
     # remove anything that's going to give us trouble during pickling 
     self.remove_unpickleable_attributes() 

    def remove_unpickleable_attributes(self): 
     for name, value in self.__dict__.items(): 
      try: 
       pickle.dumps(value) 
      except Exception: 
       del self.__dict__[name] 

import pickle 
p = pickle.dumps(PickleableModuleWrapper(pickle)) 
wrapped_mod = pickle.loads(p) 
0

Hmmm, quelque chose comme ça?

import sys 

attribList = dir(someobject) 
for attrib in attribList: 
    if(type(attrib) == type(sys)): #is a module 
     #put in a facade, either recursively list the module and do the same thing, or just put in something like str('modulename_module') 
    else: 
     #proceed with normal pickle 

De toute évidence, cela se fera via une extension de la classe de conserves au vinaigre avec une méthode de vidage réimplémentées ...

3

Vous pouvez décider et mettre en œuvre la façon dont tout type précédemment unpicklable se décapée et unpickled: voir bibliothèque standard module copy_reg (renommé en copyreg en Python 3. *). Essentiellement, vous devez fournir une fonction qui, donnée une instance du type, la réduit à un tuple - avec le même protocole que la méthode spéciale reduce (sauf que la méthode spéciale reduce ne prend aucun argument, puisque quand à condition qu'il soit appelé directement sur l'objet, alors que la fonction que vous fournissez prendra l'objet comme seul argument).

Typiquement, le tuple que vous renvoyez contient 2 éléments: un appelable et un tuple d'arguments à lui transmettre. L'appelable doit être enregistré en tant que "constructeur sûr" ou avoir de manière équivalente un attribut __safe_for_unpickling__ avec une valeur vraie. Ces éléments seront décapés, et au moment du décubitus l'appelable sera appelé avec les arguments donnés et doit renvoyer l'objet non sélectionné. Par exemple, supposons que vous souhaitiez simplement sélectionner les modules par leur nom, de sorte que leur suppression signifie simplement leur réimportation (par exemple, supposons que les modules modifiés dynamiquement, les packages imbriqués, etc. modules supérieurs de plain-pied).Puis:

>>> import sys, pickle, copy_reg 
>>> def savemodule(module): 
... return __import__, (module.__name__,) 
... 
>>> copy_reg.pickle(type(sys), savemodule) 
>>> s = pickle.dumps(sys) 
>>> s 
"c__builtin__\n__import__\np0\n(S'sys'\np1\ntp2\nRp3\n." 
>>> z = pickle.loads(s) 
>>> z 
<module 'sys' (built-in)> 

J'utilise l'ancienne forme ASCII de conserves au vinaigre de sorte que s, la chaîne contenant le cornichon, est facile à examiner: il demande à unpickling d'appeler la fonction d'importation intégrée, avec le chaîne sys comme seul argument. Et z montre que cela nous redonne en effet le module sys intégré à la suite du désépissage, comme souhaité.

Maintenant, vous devrez rendre les choses un peu plus complexes que simplement __import__ (vous devrez gérer l'enregistrement et la restauration des changements dynamiques, naviguer dans un espace de noms imbriqué, etc.), et ainsi vous devrez aussi appelez copy_reg.constructor (en passant en argument votre propre fonction qui effectue ce travail) avant la copy_reg la fonction d'économie de module qui renvoie votre autre fonction (et, si dans une exécution séparée, aussi avant que vous décuplez les pickles que vous avez faites en utilisant ladite fonction). Mais j'espère que ces cas simples aideront à montrer qu'il n'y a vraiment rien de vraiment "compliqué" intrinsèquement! -)

+0

@Alex Martelli: Quand j'utilise copy_reg.pickle, quelle est la portée dans laquelle ce changement sera pertinent? Je veux que les gens puissent importer mon travail sans changer les valeurs du système qui pourraient détruire leur programme. –

+0

'copy_reg' est global. Mais s'ils ne sont pas actuellement en train de décaper les modules (ce qui est impossible par défaut du système), ils ne peuvent pas "détruire leur programme" pour rendre les modules décapables. –

+0

@Alex Martelli: Mais s'ils rencontraient le même problème et définissaient différemment le décapage du module, nous aurions un problème. Je crois qu'il faut être poli et ne pas changer l'état du système. Je crois que lorsque vous importez un module en Python, vous ne devriez pas avoir à vous soucier de jouer avec les globals de votre système, et qu'il est important d'avoir des outils qui vous permettent d'éviter ce genre de "impolitesse" dans vos modules. –