Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Passage en paramètre d'un dictionnaire

+3 votes

J'ai le code suivant :

# -*- coding: utf-8 -*-

from re import sub, search
import csv


class MyItems:
  ''' Classe: Items '''

  adn = {
      'name': {'default': '', 'pythonise': str},
      'size': {'default': 0, 'pythonise': float},
      'status': {'default': '', 'pythonise': str},
      'options': {'default': '', 'pythonise': str},
  }

  def __init__(self, attr):
      ''' Constructed the object '''

      self.attr = self.__class__.adn.copy()

      for k, v in attr:
          if k not in self.attr.keys():
              self.attr[k] = {'default': None, 'pythonise': None}
              self.attr[k]['pythonise'] = type(v)

          self.attr[k]['default'] = self.attr[k]['pythonise'](v)



  def __str__(self):
      ''' This methods implements the object's conversion in string '''
      v = [str((k, self.attr[k]['default']))for k in self.attr.keys()]
      return ' '.join(v)



def get_liste(fic):
   ''' Retourne une liste contenant des objets MyItems '''
   COLUMN_NAME = ('name', 'status', 'options', 'size')
   maliste = []
   lfs = {}
   with open(fic, 'r') as ficcsv:
      fcsv = csv.reader(ficcsv, delimiter=' ')
      for row in fcsv:
          if row[0][0] != '':
            i = 0
            for e in row:
               print("i = ",i)
               lfs[COLUMN_NAME[i]] = e
               i += 1

            maliste.append(MyItems(lfs.items()))
            lfs = {}

   return maliste


def affiche_liste(lfs):
    ''' Affiche le contenu d'une liste de classe SystemeFichiers '''
    for e in lfs:
        print(str(e))


if __name__ == '__main__':

   fsamonter = get_liste('/data/toto')
   affiche_liste(fsamonter)

Le contenu de mon fichier de test est :

test1 1 defaults 0
test2 1 defaults 0
test3 1 defaults 0 

Au final quand j'affiche le contenu de ma liste j'obtiens ceci :

('name', 'test3') ('size', 0.0) ('options', 'defaults') ('status', '1')
('name', 'test3') ('size', 0.0) ('options', 'defaults') ('status', '1')
('name', 'test3') ('size', 0.0) ('options', 'defaults') ('status', '1')

au lieu de :

('name', 'test1') ('size', 0.0) ('options', 'defaults') ('status', '1')
('name', 'test2) ('size', 0.0) ('options', 'defaults') ('status', '1')
('name', 'test3') ('size', 0.0) ('options', 'defaults') ('status', '1')

De ce que je comprends le souci est dans le passage du contenu de lfs au moment de instanciation.
Pouvez-vous m'expliquer ce qui ne va pas ?

demandé 19-Mar-2016 par Eaudistoire (134 points)

2 Réponses

+4 votes
 
Meilleure réponse

Juste avec le résultat que tu obtiens, mon intuition est que tu passes par un référencement là où tu devrais passer par une copie profonde. Peut-être la ligne:

self.attr = self.__class__.adn.copy()

qui ne fait qu’une copie superficielle… Écris plutôt:

from copy import deepcopy
self.attr = deepcopy(self.__class__.adn)
répondu 19-Mar-2016 par etno712 (288 points)
sélectionné 19-Mar-2016 par Eaudistoire

Super ça résout mon souci.
Et si je comprends mieux, la méthode dic.copy() duplique la référence du dictionnaire et non son contenu ?

Non, ça duplique le dictionnaire lui même, mais pas les références qui sont dedans (dit autrement, ça ne duplique que le premier niveau).

+2 votes

passe ton dict dans dans le __init __, et ca va le faire:

from re import sub, search
import csv


class MyItems:
    ''' Classe: Items '''

    def __init__(self, attr):
        ''' Constructed the object '''

        adn = {
            'name': {'default': '', 'pythonise': str},
            'size': {'default': 0, 'pythonise': float},
            'status': {'default': '', 'pythonise': str},
            'options': {'default': '', 'pythonise': str},
            }
        self.attr = adn.copy()

        for k, v in attr:
            if k not in self.attr.keys():
                self.attr[k] = {'default': None, 'pythonise': None}
                self.attr[k]['pythonise'] = type(v)

            self.attr[k]['default'] = self.attr[k]['pythonise'](v)

    def __str__(self):
        ''' This methods implements the object's conversion in string '''
        v = [str((k, self.attr[k]['default'])) for k in self.attr.keys()]
        return ' '.join(v)


def get_liste(fic):
   ''' Retourne une liste contenant des objets MyItems '''
   COLUMN_NAME = ('name', 'status', 'options', 'size')
   maliste = []
   lfs = {}
   with open(fic, 'r') as ficcsv:
      fcsv = csv.reader(ficcsv, delimiter=' ')
      for row in fcsv:
          if row[0][0] != '':
            i = 0
            for e in row:
               lfs[COLUMN_NAME[i]] = e
               i += 1

            maliste.append(MyItems(lfs.items()))
            lfs = {}

   return maliste


def affiche_liste(lfs):
    ''' Affiche le contenu d'une liste de classe SystemeFichiers '''
    for e in lfs:
        print(str(e))


if __name__ == '__main__':

   fsamonter = get_liste('test.txt')
   affiche_liste(fsamonter)

ce qui me donne bien

('size', 0.0) ('options', 'defaults') ('status', '1') ('name', 'test1')
('size', 0.0) ('options', 'defaults') ('status', '1') ('name', 'test2')
('size', 0.0) ('options', 'defaults') ('status', '1') ('name', 'test3')

cf le guide ultime de Sam, part 3 sur les attributs de classes et leurs initialisations

répondu 19-Mar-2016 par ashgan (698 points)
edité 19-Mar-2016 par ashgan

Je croyais avoir compris le guide ultime de Sam. :(

Mon but en mettant le dictionnaire en attribut est de pouvoir créer une classe héritière sans avoir besoin de surcharger init()

class MyNewItems(MyItems):
  ''' Classe: Items '''

  adn = {
      'name': {'default': '', 'pythonise': str},
      'version': {'default': 0, 'pythonise': float},
      'cluster': {'default': '', 'pythonise': str},
  }

Pour tout dire, j'ai tiré cette astuce de l'article "Python le serpent très dynamique" de Jean Gabes paru dans le Linux Magazine Hors Série N°49.

init est justement fait pour :)
et bonus, ca te permets de faire ca:

self.attr = adn

c'est quand même bien plus joli que

self.attr = self.__class__.adn.copy()
...