Outils pour utilisateurs

Outils du site


sauve_recup_objets

Sauvegarder et recharger des objets Python de base

(modifié le 6/10/2009: ajout d'une 3ème méthode)

Quelques essais de sauvegarde et de récupération d'objets Python m'ont montré que cela n'était pas aussi évident que cela.

L'objectif étant, bien sûr, de récupérer un objet identique en valeur et en type! Par exemple, on sauvegarde un entier, et on veut récupérer un entier et pas une chaîne de caractère!

Sauvegarde et recharge d'objets Python avec le module shelve

Le module shelve, qui fait partie du domaine de la “persistance des données”, est très puissant, et donne un code très simple.

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
# objets a sauvegarder sur disque et à recuperer
L0=True
L1=123
L2=456L
L3=1.789e5
L4=complex(1,2)
L5="ABC"
L6='DEF'
L7=[L0,L1,L2,L3,L4,L5,L6]
L8=(L0,L1,L2,L3,L4,L5,L6)
L9={'L0':L0,'L1':L1,'L2':L2,'L3':L3,'L4':L4,'L5':L5,'L6':L6}
 
print "objets a sauvegarder:" 
print L0, type(L0)
print L1, type(L1)
print L2, type(L2)
print L3, type(L3)
print L4, type(L4)
print L5, type(L5)
print L6, type(L6)
print L7, type(L7)
print L8, type(L8)
print L9, type(L9)
print
 
#########################################################################
# Sauvegarde et récupération d'objets Python avec le module 'shelve'
# attention: la sauvegarde dans le fichier 'historiquedbm' est binaire!
 
import shelve
 
# sauvegarde des objets
d = shelve.open('historiquedbm')
d['L0'] = L0
d['L1'] = L1
d['L2'] = L2
d['L3'] = L3
d['L4'] = L4
d['L5'] = L5
d['L6'] = L6
d['L7'] = L7
d['L8'] = L8
d['L9'] = L9
d.close()
 
# récupération des objets sauvegardés
d = shelve.open('historiquedbm')
R0 = d['L0']
R1 = d['L1']
R2 = d['L2']
R3 = d['L3']
R4 = d['L4']
R5 = d['L5']
R6 = d['L6']
R7 = d['L7']
R8 = d['L8']
R9 = d['L9']
d.close()
 
print "objets recuperes par shelve:" 
print R0, type(R0)
print R1, type(R1)
print R2, type(R2)
print R3, type(R3)
print R4, type(R4)
print R5, type(R5)
print R6, type(R6)
print R7, type(R7)
print R8, type(R8)
print R9, type(R9)
print

Résultat de l'exécution:

objets a sauvegarder:
True <type 'bool'>
123 <type 'int'>
456 <type 'long'>
178900.0 <type 'float'>
(1+2j) <type 'complex'>
ABC <type 'str'>
DEF <type 'str'>
[True, 123, 456L, 178900.0, (1+2j), 'ABC', 'DEF'] <type 'list'>
(True, 123, 456L, 178900.0, (1+2j), 'ABC', 'DEF') <type 'tuple'>
{'L6': 'DEF', 'L4': (1+2j), 'L5': 'ABC', 'L2': 456L, 'L3': 178900.0, 'L0': True, 'L1': 123} <type 'dict'>

objets recuperes par shelve:
True <type 'bool'>
123 <type 'int'>
456 <type 'long'>
178900.0 <type 'float'>
(1+2j) <type 'complex'>
ABC <type 'str'>
DEF <type 'str'>
[True, 123, 456L, 178900.0, (1+2j), 'ABC', 'DEF'] <type 'list'>
(True, 123, 456L, 178900.0, (1+2j), 'ABC', 'DEF') <type 'tuple'>
{'L6': 'DEF', 'L4': (1+2j), 'L5': 'ABC', 'L2': 456L, 'L3': 178900.0, 'L0': True, 'L1': 123} <type 'dict'>

Vous voyez que les objets récupérés sont identiques en valeur et en type avec les données de départ.

Sauvegarde et recharge d'objets Python avec la méthode traditionnelle adaptée

On utilise ici la méthode traditionnelle avec ouverture de fichier par open, écriture par write, lecture par readline, etc…

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
# objets a sauvegarder sur disque et à recuperer
L0=True
L1=123
L2=456L
L3=1.789e5
L4=complex(1,2)
L5="ABC"
L6='DEF'
L7=[L0,L1,L2,L3,L4,L5,L6]
L8=(L0,L1,L2,L3,L4,L5,L6)
L9={'L0':L0,'L1':L1,'L2':L2,'L3':L3,'L4':L4,'L5':L5,'L6':L6}
 
print "objets a sauvegarder:" 
print L0, type(L0)
print L1, type(L1)
print L2, type(L2)
print L3, type(L3)
print L4, type(L4)
print L5, type(L5)
print L6, type(L6)
print L7, type(L7)
print L8, type(L8)
print L9, type(L9)
print
 
#########################################################################
# Sauvegarde et récupération d'objets Python avec la méthode traditionnelle
# le contenu du fichier de sauvegarde est éditable avec un éditeur de texte
 
f = open("historique", 'w')
 
# sauvegardes des objets
f.write(repr(L0)+'\r\n')
f.write(repr(L1)+'\r\n')
f.write(repr(L2)+'\r\n')
f.write(repr(L3)+'\r\n')
f.write(repr(L4)+'\r\n')
f.write(repr(L5)+'\r\n')
f.write(repr(L6)+'\r\n')
f.write(repr(L7)+'\r\n')
f.write(repr(L8)+'\r\n')
f.write(repr(L9)+'\r\n')
f.close()
 
f = open("historique", 'r')
 
# récupération des objets sauvegardés
R0 = eval(f.readline().rstrip('\r\n'))
R1 = eval(f.readline().rstrip('\r\n'))
R2 = eval(f.readline().rstrip('\r\n'))
R3 = eval(f.readline().rstrip('\r\n'))
R4 = eval(f.readline().rstrip('\r\n'))
R5 = eval(f.readline().rstrip('\r\n'))
R6 = eval(f.readline().rstrip('\r\n'))
R7 = eval(f.readline().rstrip('\r\n'))
R8 = eval(f.readline().rstrip('\r\n'))
R9 = eval(f.readline().rstrip('\r\n'))
f.close()
 
print "objets recuperes par open:" 
print R0, type(R0)
print R1, type(R1)
print R2, type(R2)
print R3, type(R3)
print R4, type(R4)
print R5, type(R5)
print R6, type(R6)
print R7, type(R7)
print R8, type(R8)
print R9, type(R9)
print

Ce qui donne comme résultat d'exécution:

objets a sauvegarder:
True <type 'bool'>
123 <type 'int'>
456 <type 'long'>
178900.0 <type 'float'>
(1+2j) <type 'complex'>
ABC <type 'str'>
DEF <type 'str'>
[True, 123, 456L, 178900.0, (1+2j), 'ABC', 'DEF'] <type 'list'>
(True, 123, 456L, 178900.0, (1+2j), 'ABC', 'DEF') <type 'tuple'>
{'L6': 'DEF', 'L4': (1+2j), 'L5': 'ABC', 'L2': 456L, 'L3': 178900.0, 'L0': True, 'L1': 123} <type 'dict'>

objets recuperes par open:
True <type 'bool'>
123 <type 'int'>
456 <type 'long'>
178900.0 <type 'float'>
(1+2j) <type 'complex'>
ABC <type 'str'>
DEF <type 'str'>
[True, 123, 456L, 178900.0, (1+2j), 'ABC', 'DEF'] <type 'list'>
(True, 123, 456L, 178900.0, (1+2j), 'ABC', 'DEF') <type 'tuple'>
{'L6': 'DEF', 'L4': (1+2j), 'L5': 'ABC', 'L2': 456L, 'L3': 178900.0, 'L0': True, 'L1': 123} <type 'dict'>

Vous voyez que les objets récupérés sont identiques en valeur et en type avec les données de départ. Il sont aussi identiques aux résultats de shelve, à part que le fichier de sauvegarde est consultable et éditable avec un simple éditeur de texte.

Recharge, manipulation et sauvegarde d'un dictionnaire de variables

On va utiliser la méthode précédente, mais en imitant la méthode shelve. Pour cela, on va créer une nouvelle classe.

Cette classe, appelée ici Dicovar, est dérivée par héritage d'un type dict. On pourra donc bénéficier de toutes les méthodes du type dict (dictionnaire).

Comme pour la méthode précédente, ce n'est valable que pour les types de données de base: int, long, float, complex, str, unicode, tuple, list, dict. En fait, ce sont tous les types dont on peut stocker la valeur avec repr() et dont on peut la retrouver avec eval(). Ce n'est pas le cas, par exemple, avec un type fonction: pour la fonction toto(), repr(toto) donnera quelque chose comme <function toto at 0x02A72CF0> qui donnera une erreur dans eval.

A quoi peut bien servir un code pour gérer un dictionnaire de variables? Voici un exemple: je voulais créer une calculatrice qui exploite des variables dans les expressions. Mais il s'agissait de variables de calcul, et non de variables du code de la calculatrice elle-même. Or, la fonction eval a deux paramètres optionnels intéressants: on peut ajouter un dictionnaire de variables globales et éventuellement un dictionnaire de variables locales . Prenons un exemple simplifié:

D = {'A':2, 'B':3}
print eval("A*B", D)
6

Vous voyez que le dictionnaire D a été exploité dans le calcul par eval! On pourra donc, avec le code qui suit, non seulement assurer un suivi des variables d'un calcul d'expression à l'autre, mais aussi d'une session de calcul à l'autre (grâce au stockage sur disque).

Voilà le code:

import os
 
class Dicovar(dict):
    def __init__(self, nf):
        self.nf = nf
        try:
            f = open(nf, 'r')
            dict.__init__(self, eval(f.readline().rstrip('\r\n')))
            f.close()
        except:
            dict.__init__(self, {})
 
    def enregistre(self):
        f =  open(self.nf, 'w')
        f.write(repr(self) + os.linesep)
        f.close()

L'utilisation est très simple.

A l'instanciation de la classe, il y a tentative de lecture du fichier dont on donne le nom en paramètre.

  • En cas d'erreur, c'est que le fichier n'existe pas, et on initialise le dictionnaire avec un dictionnaire vide.
  • S'il n'y a pas d'erreur, le fichier donné devrait contenir un dictionnaire de variable, et on le lit pour initialiser le dictionnaire en mémoire.

A la fin, pour enregistrer le dictionnaire en cours sur disque, il suffit d'appeler la méthode enregistre(), ce qui fait qu'on pourra le retrouver à l'identique la prochaine fois.

Entre ces deux opérations (initialisation et enregistrement), on a droit à toutes les fonctions du type dictionnaire.

Exemples:

# objets a sauvegarder sur disque et à recuperer
L0=True
L1=123
L2=456L
L3=1.789e5
L4=complex(1,2)
L5="ABC"
L6=u"DEF"
L7=[L0,L1,L2,L3,L4,L5,L6]
L8=(L0,L1,L2,L3,L4,L5,L6)
L9={'L0':L0,'L1':L1,'L2':L2,'L3':L3,'L4':L4,'L5':L5,'L6':L6}
 
print "objets a sauvegarder:" 
print L0, type(L0)
print L1, type(L1)
print L2, type(L2)
print L3, type(L3)
print L4, type(L4)
print L5, type(L5)
print L6, type(L6)
print L7, type(L7)
print L8, type(L8)
print L9, type(L9)
print
 
#########################################################################
# Sauvegarde et récupération d'objets Python avec la classe Dicovar
 
# sauvegarde des objets
d = Dicovar('testdicovar.txt')
d['L0'] = L0
d['L1'] = L1
d['L2'] = L2
d['L3'] = L3
d['L4'] = L4
d['L5'] = L5
d['L6'] = L6
d['L7'] = L7
d['L8'] = L8
d['L9'] = L9
d.enregistre()
 
# récupération des objets sauvegardés
d = Dicovar('testdicovar.txt')
R0 = d['L0']
R1 = d['L1']
R2 = d['L2']
R3 = d['L3']
R4 = d['L4']
R5 = d['L5']
R6 = d['L6']
R7 = d['L7']
R8 = d['L8']
R9 = d['L9']
d.enregistre()
 
print "objets recuperes par Dicovar" 
print R0, type(R0)
print R1, type(R1)
print R2, type(R2)
print R3, type(R3)
print R4, type(R4)
print R5, type(R5)
print R6, type(R6)
print R7, type(R7)
print R8, type(R8)
print R9, type(R9)
print

Ce qui affiche:

objets a sauvegarder:
True <type 'bool'>
123 <type 'int'>
456 <type 'long'>
178900.0 <type 'float'>
(1+2j) <type 'complex'>
ABC <type 'str'>
DEF <type 'unicode'>
[True, 123, 456L, 178900.0, (1+2j), 'ABC', u'DEF'] <type 'list'>
(True, 123, 456L, 178900.0, (1+2j), 'ABC', u'DEF') <type 'tuple'>
{'L6': u'DEF', 'L4': (1+2j), 'L5': 'ABC', 'L2': 456L, 'L3': 178900.0, 'L0': True, 'L1': 123} <type 'dict'>

objets recuperes par Dicovar
True <type 'bool'>
123 <type 'int'>
456 <type 'long'>
178900.0 <type 'float'>
(1+2j) <type 'complex'>
ABC <type 'str'>
DEF <type 'unicode'>
[True, 123, 456L, 178900.0, (1+2j), 'ABC', u'DEF'] <type 'list'>
(True, 123, 456L, 178900.0, (1+2j), 'ABC', u'DEF') <type 'tuple'>
{'L6': u'DEF', 'L4': (1+2j), 'L5': 'ABC', 'L2': 456L, 'L3': 178900.0, 'L0': True, 'L1': 123} <type 'dict'>

Comme dans les méthodes précédentes, on peut vérifier que les données sont récupérées en valeur et en type.

Petits compléments: comme grâce à l'héritage on peut utiliser les méthodes du type dictionnaire:

Remise à “dictionnaire vide” du fichier:

s = Dicovar('testdicovar.txt')
s.clear()
s.enregistre()

Réinitialisation en cours d'exécution avec un autre dictionnaire de variables (appelé ici D):

s = Dicovar('testdicovar.txt')
s.clear()
s.update(D)
s.enregistre()

Etc…

Amusez-vous bien!

sauve_recup_objets.txt · Dernière modification: 2009/10/06 08:57 par tyrtamos

Outils de la page