Nous souhaitons récemment archiver les résultats de téléchargement obtenus par le robot. Ce résultat est un objet Python (nous ne voulons pas simplement enregistrer un HTML ou un json, nous voulons réaliser l'intégralité du processus de téléchargement. réversible), j'ai donc pensé à utiliser la bibliothèque de cornichons intégrée de Python (bibliothèque de concombre mariné) pour sérialiser les objets en octets et les désérialiser si nécessaire.
Vous pouvez simplement comprendre l'utilisation et la fonction de pickle grâce au code suivant.
In [2]: import pickle In [3]: class A: pass In [4]: a = A() In [5]: a.foo = 'hello' In [6]: a.bar = 2 In [7]: pick_ed = pickle.dumps(a) In [8]: pick_ed Out[8]: b'\x80\x03c__main__\nA\nq\x00)\x81q\x01}q\x02(X\x03\x00\x00\x00fooq\x03X\x05\x00\x00\x00helloq\x04X\x03\x00\x00\x00barq\x05K\x02ub.' In [9]: unpick = pickle.loads(pick_ed) In [10]: unpick Out[10]: <__main__.A at 0x10ae67278> In [11]: a Out[11]: <__main__.A at 0x10ae67128> In [12]: dir(unpick) Out[12]: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slotnames__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo'] In [13]: unpick.foo Out[13]: 'hello' In [14]: unpick.bar Out[14]: 2
Vous pouvez voir que l'utilisation de pickle est quelque peu similaire à celle de json, mais il existe plusieurs différences fondamentales :
json est un format d'échange de données universel multilingue, généralement exprimé sous forme de texte, lisible par les humains. pickle est utilisé pour sérialiser des objets Python, uniquement pour Python. Le résultat de la sérialisation est des données binaires, qui ne sont pas lisibles par les humains. De plus, json ne peut sérialiser qu'une partie des types intégrés par défaut, et pickle peut sérialiser un grand nombre de données.
Il y a aussi un ancien maréchal également intégré. Mais cette bibliothèque est principalement destinée aux fichiers .pyc. Les types personnalisés ne sont pas pris en charge et ne sont pas complets. Par exemple, il ne peut pas gérer les applications cycliques. Si un objet fait référence à lui-même, l'interpréteur Python se bloquera lors de l'utilisation de marshal.
Problèmes de compatibilité des versions
Puisque pickle est pour Python, Python a différentes versions (et la différence entre 2 et 3 est très grande), il faut donc en tenir compte considération L'objet sérialisé peut-il être désérialisé par une version supérieure (ou inférieure ?) de Python.
Il existe actuellement 5 versions du protocole pickle. Plus la version est élevée, plus la version Python 0-2 est élevée pour Python2 et 3-4 pour Python3.
Protocol version 0 is the original “human-readable” protocol and is backwards compatible with earlier versions of Python.Protocol version 1 is an old binary format which is also compatible with earlier versions of Python.Protocol version 2 was introduced in Python 2.3. It provides much more efficient pickling of new-style classes. Refer to PEP 307for information about improvements brought by protocol 2. (从这个版本往后,性能有显著提高)Protocol version 3 was added in Python 3.0. It has explicit support for bytes objects and cannot be unpickled by Python 2.x.This is the default protocol, and the recommended protocol when compatibility with other Python 3 versions is required.Protocol version 4 was added in Python 3.4. It adds support for very large objects, pickling more kinds of objects, and some data format optimizations. Refer to PEP 3154 for information about improvements brought byprotocol 4.
La plupart des. les entrées des fonctions pickle (telles que dump(), dumps(), le constructeur Pickler) acceptent toutes un paramètre de version de protocole, qui a deux variables intégrées :
pickle.HIGHEST_PROTOCOL est actuellement 4
pickle. DEFAULT_PROTOCOL est actuellement 3
L'utilisation
est similaire à l'interface du module json intégré, dumps() est utilisé pour renvoyer les résultats de sérialisation, dump() est utilisé pour sérialiser puis écrire Importer le fichier. De la même manière, il existe Load() et Loads(). Parmi eux, vous pouvez spécifier la version du protocole lors de la sérialisation des dump(s). Cela n'est pas requis lors de la désérialisation et la version sera automatiquement identifiée. Ceci est très similaire à la commande zip.
Sérialisation des types intégrés
La plupart des types intégrés prennent en charge la sérialisation et la désérialisation. Ce qui nécessite une attention particulière, ce sont les fonctions. La sérialisation d'une fonction se base uniquement sur son nom et le module dans lequel elle se trouve. Ni le code de la fonction ni ses attributs (les fonctions de Python sont des objets de première classe et peuvent avoir des attributs) ne seront sérialisés. Cela nécessite que le module dans lequel se trouve la fonction soit importable dans l'environnement unpickle, sinon ImportError ou AttributeError se produira.
Il y a quelque chose d'intéressant ici : toutes les fonctions lambda ne sont pas Pickleable. Parce que leurs noms s'appellent
Sérialisation des types personnalisés
Comme le code expérimental au début de cet article, dans la plupart des cas aucune opération supplémentaire ne peut réaliser l'opération de sérialisation/désérialisation . A noter que lors du processus de désérialisation, le __init__() de la classe n'est pas appelé pour initialiser un objet, mais une nouvelle instance non initialisée est créée puis ses attributs sont restaurés (très astucieux). Le pseudocode est le suivant :
def save(obj): return (obj.__class__, obj.__dict__) def load(cls, attributes): obj = cls.__new__(cls) obj.__dict__.update(attributes) return obj
Si vous souhaitez effectuer certaines opérations supplémentaires pendant le processus de sérialisation, comme sauvegarder l'état de l'objet, vous pouvez utiliser les méthodes magiques du protocole pickle, les plus courantes sont __setstate__() et __getstate__( ).
Problèmes de sécurité (!)
Le début de la documentation de pickle dit : Ne dépicklez jamais un binaire provenant d'une source inconnue. Considérez le code suivant :
>>> import pickle >>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.") hello world 0
Une fois décoché, ce code importe os.system() puis appelle echo. Il n'y a aucun effet secondaire. Mais que se passe-t-il si c'est rm -rf /· ?
La suggestion donnée par le document est d'implémenter la logique de vérification dans Unpickler.find_class(). Les méthodes de fonction doivent être appelées lorsque des variables globales sont requises.
import builtins import io import pickle safe_builtins = { 'range', 'complex', 'set', 'frozenset', 'slice', } class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): # Only allow safe classes from builtins. if module == "builtins" and name in safe_builtins: return getattr(builtins, name) # Forbid everything else. raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name)) def restricted_loads(s): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO(s)).load()
Compression
Il ne se compressera pas automatiquement après le décapage. Je pense que ce design est très bon. Il est découplé. . Les choses, la compression est laissée aux autres bibliothèques. Et vous pouvez également constater que même si le fichier après décapage est illisible, le contenu est toujours présenté en code ASCII et n'est pas tronqué. Vous devez appeler compress de la bibliothèque de compression. Après compression réelle, le volume est d'environ 1/3 du précédent, ce qui est très impressionnant.
Résumé
Il est un peu difficile de garder des variables globales importables. La question à laquelle je dois faire face est la suivante : si je dois ouvrir les choses que j'ai marinées aujourd'hui à l'avenir, puis-je toujours les ouvrir ?
Il existe plusieurs versions ici : la version du projet, la version python, la version du protocole pickle et les versions du package dont dépend le projet. Parmi eux, je pense que la version python et la version pickle peuvent compter en toute sécurité sur leur rétrocompatibilité et sont faciles à résoudre. Principalement le projet et la version et les versions dépendantes. Si l'objet à récupérer est très complexe, alors il est probable que la sauvegarde de l'ancienne version ne soit pas compatible avec la nouvelle version. Une solution possible consiste à verrouiller complètement toutes les dépendances, par exemple en enregistrant leurs valeurs de hachage. Si vous souhaitez restaurer une certaine séquence binaire, restaurez les dépendances spécifiques et les validations spécifiques du projet à ce moment-là.
Mais pour l'instant, notre exigence est essentiellement de décaper un objet request.Response. Je pense que nous pouvons compter sur leur rétrocompatibilité. Si un jour il y a un changement radical dans les requêtes, alors même si notre cornichon est compatible, le code ne sera pas compatible. À ce moment-là, d'autres stratégies peuvent être envisagées.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!