Partage
  • Partager sur Facebook
  • Partager sur Twitter

Création d'une classe (dict-like) pour parser un fichier!

    7 décembre 2010 à 21:25:05

    Bonsoir!


    J'aimerais créer une classe pour gérer simplement mes fichiers de configurations personnels, le problème c'est que j'ai aucune idée de comment organiser ça, sachant qu'il devra agir comme un dictionnaire. Enfin un exemple c'est toujours plus clair!

    Exemple d'un fichier de configuration :
    12|04|2010|12:33|5|121|1|4|0|5|0
    12|04|2010|12:54|5|139|2|4|0|4|0
    12|04|2010|13:10|5|147|4|1|0|5|0
    12|04|2010|13:21|5|94|1|2|0|7|0
    

    La syntaxe est la suivante :
    month|day|year|hour:minutes|piste|score|strike|spare|split|null|hcp
    


    J'aimerais pouvoir l'utiliser comme ceci :
    >>> bwl = BowlingFile(<filepath>)
    >>> print bwl[0]
    {"month":"12", "day":"04", "year":"2010", "hour":"12:33", "piste":"5", "score":"121", "strike":"1", "spare":"4", "split":"0", "null":"5", "hcp":"0"}
    >>> bwl[0]["piste"] = "12"
    


    Je sais pas du tout comment faire pour que lorsque j'écris dans le fichier, les infos soient toujours dans le même ordre, comment agencer les méthodes __setitem__ et __getitem__ pour que ça modifie automatiquement le contenu et ça l'écrit dans le fichier ...

    J'ai fait une ébauche de code, mais franchement rien de fameux :
    class BowlingFile(object):
    	def __init__(self, filepath):
    		self.filepath = filepath
    		self.content  = self._parse()
    		
    	def _parse(self):
    		returnDict = dict()
    		with open(self.filepath, 'r') as fileStream:
    			lines = fileStream.read().splitlines()
    			for line in lines:
    				if line.startwith('#'):
    					continue
    				month, day, year, time, piste, score, strike, spare, split, null, hcp = line.split('|')
    				returnDict[len(returnDict)] = {'month':month, 
                                                   'day':day, 
                                                   'year':year, 
                                                   'time':time, 
                                                   'piste':piste, 
                                                   'score':score, 
                                                   'strike':strike, 
                                                   'spare':spare, 
                                                   'split':split, 
                                                   'null':null, 
                                                   'hcp':hcp}
    			return returnDict
    		
    	def _write(self):
    		with open(self.filepath, 'w') as fileStream:
    			for key in self.content:
    				# self.write('%s\r\n' % '|'.join())
    


    Et un petit code pour voir comment je pensais agencer ça :
    filepath = 'C://scores.bwl'
    fileobj = open(filepath, 'r')
    mylist = [line for line in fileobj.read().splitlines() if not line.startswith('#')]
    mydict = dict()
    
    for x in mylist:
    	month, day, year, time, piste, score, strike, spare, split, null, hcp = x.split('|')
    	mydict[len(mydict)] = {'month':month, 'day':day, 'year':year, 'time':time, 'piste':piste, 'score':score, 'strike':strike, 'spare':spare, 'split':split, 'null':null, 'hcp':hcp}
    
    for key in mydict:
    	print '%s\n\t' % key,
    	for subkey in mydict[key]:
    		print '%s \t: %s\n\t' % (subkey, mydict[key][subkey]),
    	print '\n',
    
    0
    	month 	: 12
    	piste 	: 5
    	year 	: 2010
    	null 	: 5
    	day 	: 04
    	hcp 	: 0
    	score 	: 121
    	split 	: 0
    	time 	: 12:33
    	strike 	: 1
    	spare 	: 4
    	
    1
    	month 	: 12
    	piste 	: 5
    	year 	: 2010
    	null 	: 4
    	day 	: 04
    	hcp 	: 0
    	score 	: 139
    	split 	: 0
    	time 	: 12:54
    	strike 	: 2
    	spare 	: 4
    	
    2
    	month 	: 12
    	piste 	: 5
    	year 	: 2010
    	null 	: 5
    	day 	: 04
    	hcp 	: 0
    	score 	: 147
    	split 	: 0
    	time 	: 13:10
    	strike 	: 4
    	spare 	: 1
    	
    3
    	month 	: 12
    	piste 	: 5
    	year 	: 2010
    	null 	: 7
    	day 	: 04
    	hcp 	: 0
    	score 	: 94
    	split 	: 0
    	time 	: 13:21
    	strike 	: 1
    	spare 	: 2
    


    En espérant qu'on puisse m'aider,

    FMIS@Menace.
    • Partager sur Facebook
    • Partager sur Twitter
      7 décembre 2010 à 22:20:23

      Pour les dates, j'aurais utilisé un timestamp, ça prend moins de place et se traite avec plus de facilité. Ça va aussi accélérer le chargement puisqu'il y aura moins de champs à trouver.

      Ensuite, si tu veux juste une liste de dico, pourquoi utiliser une classe ? Une simple fonction qui charge le fichier à coup de 'split("|")' et retourne une liste de dico toute bête ?
      • Partager sur Facebook
      • Partager sur Twitter
        7 décembre 2010 à 22:21:37

        a = '12|04|2010|12:33|5|121|1|4|0|5|0'.split('|')
        b = 'month|day|year|hour:minutes|piste|score|strike|spare|split|null|hcp'.split('|')
        c = dict(zip(b,a))
        print c
        

        {'hcp': '0', 'hour:minutes': '12:33', 'month': '12', 'score': '121', 'piste': '5', 'split': '0', 'year': '2010', 'strike': '1', 'spare': '4', 'null': '5', 'day': '04'}
        • Partager sur Facebook
        • Partager sur Twitter

        Python c'est bon, mangez-en. 

          7 décembre 2010 à 22:47:37

          Citation : bobibou

          Pour les dates, j'aurais utilisé un timestamp, ça prend moins de place et se traite avec plus de facilité. Ça va aussi accélérer le chargement puisqu'il y aura moins de champs à trouver.

          Ensuite, si tu veux juste une liste de dico, pourquoi utiliser une classe ? Une simple fonction qui charge le fichier à coup de 'split("|")' et retourne une liste de dico toute bête ?



          Non, étant donné que c'est pour renseigner des parties de bowling pour calculer un ratio.

          Car j'aimerais que le fichier soit, à chaque changement, réécrit totalement, et que ce soit facile à traiter.

          josmiley : Merci pour le coup du zip(), ca m'aide pas mal!

          J'aimerais savoir maintenant comment, quand je réécris le fichier, que ce soit écrit dans le même ordre, et si on peut m'aider pour les méthodes __get/setitem__ ?
          • Partager sur Facebook
          • Partager sur Twitter
            7 décembre 2010 à 23:31:05

            mydict = {'hcp': '0', 'hour:minutes': '12:33', 'month': '12', 'score': '121', 'piste': '5', 'split': '0', 'year': '2010', 'strike': '1', 'spare': '4', 'null': '5', 'day': '04'}
            output = '|'.join([mydict[item] for item in 'month', 'day', 'year', 'hour:minutes', 'piste', 'score', 'strike', 'spare', 'split', 'null', 'hcp'])
            print output
            

            '12|04|2010|12:33|5|121|1|4|0|5|0'
            • Partager sur Facebook
            • Partager sur Twitter

            Python c'est bon, mangez-en. 

              7 décembre 2010 à 23:36:27

              Citation : josmiley

              mydict = {'hcp': '0', 'hour:minutes': '12:33', 'month': '12', 'score': '121', 'piste': '5', 'split': '0', 'year': '2010', 'strike': '1', 'spare': '4', 'null': '5', 'day': '04'}
              output = '|'.join([mydict[item] for item in 'month', 'day', 'year', 'hour:minutes', 'piste', 'score', 'strike', 'spare', 'split', 'null', 'hcp'])
              print output
              


              '12|04|2010|12:33|5|121|1|4|0|5|0'


              Ca marche niquel, merci beaucoup!

              Pour finir, personne a une idée pour le __set/getitem__ ?
              • Partager sur Facebook
              • Partager sur Twitter
                7 décembre 2010 à 23:46:16

                tu veux que, quand tu modifies mydict, ça enregistre automatiquement le fichier, c'est ça ?
                peut-être en dérivant de dict et en réécrivant __setitem__ ?
                • Partager sur Facebook
                • Partager sur Twitter

                Python c'est bon, mangez-en. 

                  8 décembre 2010 à 0:03:44

                  Citation : josmiley

                  tu veux que, quand tu modifies mydict, ça enregistre automatiquement le fichier, c'est ça ?
                  peut-être en dérivant de dict et en réécrivant __setitem__ ?



                  Oui c'est exactement ça

                  Justement j'y ai pensé, mais je vois pas du tout comment écrire ça ! (J'ai fait un bout de code, suffit de regarder au-dessus)
                  • Partager sur Facebook
                  • Partager sur Twitter
                    8 décembre 2010 à 0:07:37

                    ça peut aider ?
                    class foo(dict):
                        ''
                        def __setitem__(self,i,j):
                            print 'nom de la cle:',i
                            print 'valeur de la cle:',j
                    
                    foo()['my_key']=17
                    
                    nom de la cle: my_key
                    valeur de la cle: 17

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Python c'est bon, mangez-en. 

                      8 décembre 2010 à 0:12:50

                      Le problème est que c'est self.content[x][y] = z que j'aimerais capturer, si c'est possible !

                      En passant, y'a moyen de simplifier la ligne 37 ?

                      Code actuel :
                      ''' ./widgets/cfglib.py '''
                      
                      from __future__ import with_statement
                      
                      import os.path
                      
                      class BowlingFile(object):
                      	syntax = 'month|day|year|time|piste|score|strike|spare|split|null|hcp'.split('|')
                      	def __init__(self, filepath):
                      		self.filepath = filepath
                      		self.content  = self._parse()
                      		
                      	def _parse(self):
                      		returnDict = dict()
                      		with open(self.filepath, 'r') as fileStream:
                      			lines = fileStream.read().splitlines()
                      			for line in lines:
                      				if line.startswith('#'):
                      					continue
                      				returnDict[len(returnDict)] = dict(zip(self.syntax, line))
                      			return returnDict
                      		
                      	def _write(self):
                      		with open(self.filepath, 'w') as fileStream:
                      			for key in self.content:
                      				fileStream.write('%s\r\n' % '|'.join(self.content[key][item] for item in self.syntax))
                      				
                      	def addGame(self, *args):
                      		if len(args) == len(self.syntax):
                      			self.content[len(self.content)] = dict(zip(self.syntax, '|'.join(args)))
                      			self._write()
                      		else:
                      			raise TypeError, 'addGame expected at %s arguments, got %s' % (len(syntax), len(args))
                      			
                      	def removeGame(self, month, day, year, time):
                      		for key in self.content:
                      			if self.content[key]['month'] == month and self.content[key]['day'] == day and self.content[key]['year'] == year and self.content[key]['time'] == time:
                      				del self.content[key]
                      				return
                      		raise IndexError, 'Game %s-%s-%s-%s cannot be found in file \'%s\'' % (month, day, year, time, self.filepath)
                      
                      • Partager sur Facebook
                      • Partager sur Twitter
                        8 décembre 2010 à 0:18:59

                        Citation : FMIS@Menace.

                        Le problème est que c'est self.content[x][y] = z que j'aimerais capturer, si c'est possible !


                        ben, self.content[x] est un objet de ta class dérivée de dict ...

                        KEY = self.content[key]
                        if (KEY['month'],KEY['day'],KEY['year'],KEY['time']) == (month,day,year,time):
                        

                        • Partager sur Facebook
                        • Partager sur Twitter

                        Python c'est bon, mangez-en. 

                          8 décembre 2010 à 0:21:36

                          Bah quand je mets __setitem__ dans ma classe, avec un print key, value, ça me renvoie rien quand je modifie mes valeurs!

                          Merci pour le code plus court btw!
                          • Partager sur Facebook
                          • Partager sur Twitter
                            8 décembre 2010 à 0:26:14

                            Citation : FMIS@Menace.

                            Bah quand je mets __setitem__ dans ma classe, avec un print key, value, ça me renvoie rien quand je modifie mes valeurs!



                            tu dois créer une 2eme class ...
                            et remplacer
                            def _parse(self):
                            	returnDict = dict()
                            

                            par
                            def _parse(self):
                            	returnDict = ma_class_derivee_de_dict()
                            

                            c'est ma_class_derivee_de_dict qui contient le nouveau __setitem__.
                            • Partager sur Facebook
                            • Partager sur Twitter

                            Python c'est bon, mangez-en. 

                              8 décembre 2010 à 0:27:43

                              Ok, merci!

                              EDIT: Voici mon code, j'ai trouvé pour __setitem__ donc, le problème est comment je fais pour accéder à la méthode _write de la classe BowlingFile, celle-ci prenant comme argument le répertoire du fichier ?
                              ''' ./widgets/cfglib.py '''
                              
                              from __future__ import with_statement
                              
                              import os.path
                              
                              class BowlingFile(object):
                              	class GameDict(dict):
                              		def __setitem__(self, key, value):
                              			# ...
                              			super(GameDict, self).__setitem__(key, value)
                              	
                              	syntax = 'month|day|year|time|piste|score|strike|spare|split|null|hcp'.split('|')
                              	def __init__(self, filepath):
                              		self.filepath = filepath
                              		self.content  = self._parse()
                              		
                              	def _parse(self):
                              		returnDict = dict()
                              		with open(self.filepath, 'r') as fileStream:
                              			lines = fileStream.read().splitlines()
                              			for line in lines:
                              				if line.startswith('#'):
                              					continue
                              				returnDict[len(returnDict)] = self.GameDict(zip(self.syntax, line.split('|')))
                              			return returnDict
                              		
                              	def _write(self):
                              		with open(self.filepath, 'w') as fileStream:
                              			for key in self.content:
                              				fileStream.write('%s\r\n' % '|'.join(self.content[key][item] for item in self.syntax))
                              				
                              	def addGame(self, *args):
                              		if len(args) == len(self.syntax):
                              			self.content[len(self.content)] = dict(zip(self.syntax, args))
                              			self._write()
                              		else:
                              			raise TypeError, 'addGame expected at %s arguments, got %s' % (len(self.syntax), len(args))
                              			
                              	def removeGame(self, month, day, year, time):
                              		for key in self.content:
                              			if (self.content[key]['month'], self.content[key]['day'], self.content[key]['year'], self.content[key]['time']) == (month, day, year, time):
                              				del self.content[key]
                              				return
                              		raise IndexError, 'Game %s-%s-%s-%s cannot be found in file \'%s\'' % (month, day, year, time, self.filepath)
                              
                              • Partager sur Facebook
                              • Partager sur Twitter
                                8 décembre 2010 à 1:32:23

                                tu passes soit le nom du fichier à chaques objets GameDict, soit comme dans l'exemple ci-dessous, l'objet BowlingFile lui même ... ligne 17.
                                class master(list):
                                    ''
                                    class foo(dict):
                                        ''
                                        def __init__(self,master):
                                            self.master = master
                                        def __setitem__(self,i,j):
                                            print 'nom du fichier:',self.master.filename
                                            print 'nom de la cle:',i
                                            print 'valeur de la cle:',j
                                            super(master.foo,self).__setitem__(i,j)
                                            print 'master vaut apres modif:',self.master
                                            
                                    def __init__(self,filename):
                                        self.filename = filename
                                        for item in range(5):
                                            self.append(self.foo(self))
                                
                                
                                M = master('mon_fichier')
                                M[0]['item1'] = 27
                                
                                • Partager sur Facebook
                                • Partager sur Twitter

                                Python c'est bon, mangez-en. 

                                  8 décembre 2010 à 12:55:13

                                  Hmm, j'ai eu un peu la flemme de tout lire, mais les namedtuples semblent bien adaptés à tes données (données groupées, nommées et immutables).
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  Zeste de Savoir, le site qui en a dans le citron !
                                    8 décembre 2010 à 13:04:57

                                    Citation : NoHaR

                                    Hmm, j'ai eu un peu la flemme de tout lire, mais les namedtuples semblent bien adaptés à tes données (données groupées, nommées et immutables).



                                    Je pense que je vais en rester aux dictionnaires pour l'instant, mais je vais regarder ça petit à petit.

                                    Maxibolt : Comme je l'ai dit, je pense en rester aux dictionnaires!

                                    josmiley : Je vois pas comment inclure ça dans mon code vraiment, et je suis pas sûr que ça marche.

                                    Pour récapituler, j'utilise un dictionnaire (self.content) dans la classe BowlingFile qui contient des clefs digitales, contenant chacune un dictionnaire de type GameDict. J'aimerais que à chaque fois qu'une clef change ou est créé dans un objet de type GameDict, qu'il soit mise à jour dans le dictionnaire self.content de la classe BowlingFile, et qu'il l'écrit dans un fichier.

                                    Le code actuel :
                                    ''' ./widgets/cfglib.py '''
                                    
                                    from __future__ import with_statement
                                    
                                    import sys
                                    
                                    class GameDict(dict):
                                    	def __setitem__(self, key, value):
                                    		# Modify BowlingFile.content and access to BowlingFile(<path>).write() ...
                                    		super(GameDict, self).__setitem__(key, value)
                                    
                                    class BowlingFile(object):
                                    	""" Class for handling .bwl files """
                                    	syntax = "month|day|year|time|piste|score|strike|spare|split|null|hcp".split("|")
                                    	def __init__(self, filepath):
                                    		"""" 
                                    		Default constructor, initialize variables 
                                    		
                                    		@PARAM str filepath - The file to parse
                                    		"""
                                    		self.filepath = filepath
                                    		self.content = self.parse()
                                    		
                                    	def parse(self):
                                    		"""
                                    		Static method to parse the file
                                    		
                                    		@RETURN dict - Dictionnary using the Class"s syntax
                                    		"""
                                    		returnDict = dict()
                                    		with open(self.filepath, "r") as fileStream:
                                    			lines = fileStream.read().splitlines()
                                    			for line in lines:
                                    				if line.startswith("#") or not len(line):
                                    					continue
                                    				returnDict[len(returnDict)] = GameDict(zip(self.syntax, line.split("|")))
                                    			return returnDict
                                    		
                                    	def write(self):
                                    		"""
                                    		Static method to write the Class"s content into the file, using the Class"s syntax
                                    		"""
                                    		with open(self.filepath, "w") as fileStream:
                                    			for key in self.content:
                                    				fileStream.write("%s%s" % ("|".join(self.content[key][item] for item in self.syntax), "\n" if sys.platform == "win32" else "\r\n"))
                                    				
                                    	def addGame(self, *args):
                                    		"""
                                    		Add a Game into the Class"s content
                                    		
                                    		@PARAM tuple args - Class"s syntax related args, raise a TypeError if not
                                    		"""
                                    		if len(args) == len(self.syntax):
                                    			self.content[len(self.content)] = dict(zip(self.syntax, args))
                                    			self.write()
                                    		else:
                                    			raise TypeError, "addGame expected at %s arguments, got %s" % (len(self.syntax), len(args))
                                    			
                                    	def removeGame(self, month, day, year, time):
                                    		"""
                                    		Remove a Game from the Class"s content
                                    		
                                    		@PARAM str month - The month of the game to remove
                                    		@PARAM str day   - The day of the game to remove
                                    		@PARAM str year  - The year of the game to remove
                                    		@PARAM str time  - The time of the game to remove (h:m)
                                    		
                                    		Raise a IndexError if game doesn"t exist
                                    		"""
                                    		for key in self.content:
                                    			if (self.content[key]["month"], self.content[key]["day"], self.content[key]["year"], self.content[key]["time"]) == (month, day, year, time):
                                    				del self.content[key]
                                    				self.write()
                                    				return
                                    		raise IndexError, "Game %s-%s-%s-%s cannot be found in file '%s'" % (month, day, year, time, self.filepath)
                                    
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      8 décembre 2010 à 14:02:46

                                      Citation : Maxibolt

                                      Tu as regardé le JSON ?



                                      Il n'y a aucun intérêt à réinventer un format de fichier et le parser qui va avec, plutôt que d'utiliser json ou yaml qui règlent le problème en 2 lignes de code...
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        8 décembre 2010 à 14:09:13

                                        Citation : Lord Casque Noir

                                        Citation : Maxibolt

                                        Tu as regardé le JSON ?



                                        Il n'y a aucun intérêt à réinventer un format de fichier et le parser qui va avec, plutôt que d'utiliser json ou yaml qui règlent le problème en 2 lignes de code...



                                        Si c'est le cas, y'a moyen de me faire un exemple avec mon fichier et ma syntaxe s'il te plaît ?
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          8 décembre 2010 à 14:33:49

                                          Citation : FMIS@Menace.

                                          Si c'est le cas, y'a moyen de me faire un exemple avec mon fichier et ma syntaxe s'il te plaît ?



                                          import json
                                          
                                          json.dump( data, open( "bidon.json", "w" ))
                                          data2 = json.load( open( "bidon.json" ))
                                          


                                          Le mieux dans ton cas est d'utiliser yaml, car json ne peut pas sauvegarder directement des objets datetime par exemple. Yaml le fait automatiquement (et aussi pour tout un tas d'autres types) donc c'est extrêmement pratique.

                                          import yaml
                                          
                                          yaml.dump( data, open( "bidon.yaml", "w" ))
                                          data2 = yaml.load( open( "bidon.yaml" ))
                                          


                                          Bien sûr ça change le format de fichier, mais ce n'est pas important, puisque c'est toi qui définis le format, donc tu mets ce que tu veux dedans. Autant utiliser un vrai format.

                                          Sinon, voici un parser (très incomplet) pour ton format de fichier :

                                          import pprint
                                          
                                          # parser un format de fichier ad-hoc
                                          fields = "month", "day", "year", "time", "piste", "score", "strike", "spare", "split", "null", "hcp"
                                          data  = [ dict( zip( fields, line.strip().split( "|" ))) for line in open( "bidon.txt" ) if not line.isspace() ]
                                          
                                          pprint.pprint( data )
                                          


                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            8 décembre 2010 à 21:09:28

                                            JSON, *c'est* des dictionnaires.
                                            • Partager sur Facebook
                                            • Partager sur Twitter

                                            Création d'une classe (dict-like) pour parser un fichier!

                                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                            • Editeur
                                            • Markdown