Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1638 connectés 

  FORUM HardWare.fr
  Programmation
  Python

  Comportement d'une boucle sur un tableau modifié dans la boucle

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Comportement d'une boucle sur un tableau modifié dans la boucle

n°1624217
Sve@r
Posté le 15-10-2007 à 21:36:57  profilanswer
 

Salut à tous,
Je continue ma découverte de Python et évidemment chaque jour j'en découvre un peu plus.
Là, j'ai été confronté à un problème qui m'a bloqué pendant un quart d'heure avant que je ne comprenne d'où venait le bug. En fait, j'avais instancié une boucle sur un tableau pour en éliminer certains éléments et ma boucle n'éliminait pas les bons
Ex:

Code :
  1. tab=['a', 'z', 'e', 'r', 't']
  2.  
  3. for i, c in enumerate(tab):
  4.    print i, c, tab
  5.    if (i % 2) == 0:
  6.        tab.remove(c)
  7.  
  8. # Résultat
  9. # 0 a ['a', 'z', 'e', 'r', 't']    => Normal, on est à la première itération => c vaut 'a' et le 'a' sera supprimé
  10. # 1 e ['z', 'e', 'r', 't']         => Bizarre, on est à la seconde itération, c devrait valoir 'z' mais il vaut 'e'
  11. # 2 r ['z', 'e', 'r', 't']         => Bizarre, on est à la troisième itération, c devrait valoir 'e' mais il vaut 'r'
  12. # Et en plus il ne traite pas le 't'
  13.  
  14. print tab
  15. # ['z', 'e', 't']


Il semble qu'à chaque itération de la boucle, le tableau est réévalué dans son intégralité. Donc à la seconde itération c vaut le 2° élément du tableau actuel soit 'e'. Et à la 3°, il vaut le 3° élément soit 'r'. Et comme cette lettre est supprimée, le nombre d'éléments passe à 3 et comme j'ai atteint la 3° itération Python considère que j'ai traité tout mon tableau. C'est ça ???


Message édité par Sve@r le 15-10-2007 à 21:41:16

---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
mood
Publicité
Posté le 15-10-2007 à 21:36:57  profilanswer
 

n°1624229
masklinn
í dag viðrar vel til loftárása
Posté le 15-10-2007 à 21:49:02  profilanswer
 

On modifie pas un tableau quand on itère dessus via un iterator en python.

 

Jamais.

 

Si tu veux filtrer, tu utilises soit filter soir une list comprehension soit une génération manuelle de liste de sortie (à base de for et de list.append):

 
Code :
  1. >>> tab=['a', 'z', 'e', 'r', 't']
  2. >>> out = [c for i, c in enumerate(tab) if i%2 != 0]
  3. >>> out
  4. ['z', 'r']
  5. >>>


Citation :

Il semble qu'à chaque itération de la boucle, le tableau est réévalué dans son intégralité. Donc à la seconde itération c vaut le 2° élément du tableau actuel soit 'e'. Et à la 3°, il vaut le 3° élément soit 'r'. Et comme cette lettre est supprimée, le nombre d'éléments passe à 3 et comme j'ai atteint la 3° itération Python considère que j'ai traité tout mon tableau. C'est ça ???


En gros, Python génère un iterator qui garde simplement un index sur l'élément courant du tableau, sans rien faire d'autre.

 

En décomposé, si on appelle cet index caché __index__:

 
Code :
  1. for c, i in enumerate(tab): # __index__ = 0, tab = ['a', 'z', 'e', 'r', 't'], c = 'a'
  2.    if (i % 2) == 0: # true (i == 0)
  3.        tab.remove(c) # tab = ['z', 'e', 'r', 't']
  4. for c, i in enumerate(tab): # __index__ = 1, tab = ['z', 'e', 'r', 't'], c = 'e'
  5.    if (i % 2) == 0: # false (i == 1)
  6.        pass
  7. for i, c in enumerate(tab): # __index__ = 2, tab = ['z','e','r','t'], c = 'r'
  8.   if (i % 2) == 0: # true (i == 2)
  9.       tab.remove(c) # tab = ['z','e','t']
  10. for i, c in enumerate(tab): # __index__ = 3, tab = ['z','e','t'], fin de l'itération
  11.   pass
 

   

Message cité 1 fois
Message édité par masklinn le 15-10-2007 à 21:56:25

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
n°1624277
Sve@r
Posté le 15-10-2007 à 22:39:43  profilanswer
 

masklinn a écrit :

Si tu veux filtrer, tu utilises soit filter soir une list comprehension soit une génération manuelle de liste de sortie (à base de for et de list.append):
 

Code :
  1. >>> tab=['a', 'z', 'e', 'r', 't']
  2. >>> out = [c for i, c in enumerate(tab) if i%2 != 0]
  3. >>> out
  4. ['z', 'r']
  5. >>>



Oui, j'ai effectivement pensé à créer une 2° liste contenant mes éléments à supprimer puis en itérant cette liste et en appelant tab.remove(). Mais quelque part ça me gêne de devoir dupliquer mon tableau pour le remover. Ca peut pas marcher si je crée un indice perso qui n'évolue que quand il faut style

Code :
  1. i=0
  2. lg=len(tab)
  3. for j in range(lg):
  4.    if tab[i] != ce que je veux:
  5.        del tab[i]
  6.    else
  7.        i+=1


???


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
n°1624278
masklinn
í dag viðrar vel til loftárása
Posté le 15-10-2007 à 22:46:03  profilanswer
 

Sve@r a écrit :


Oui, j'ai effectivement pensé à créer une 2° liste contenant mes éléments à supprimer puis en itérant cette liste et en appelant tab.remove().


Mais de quoi tu parles [:mlc]

Sve@r a écrit :

Mais quelque part ça me gêne de devoir dupliquer mon tableau pour le remover. Ca peut pas marcher si je crée un indice perso qui n'évolue que quand il faut style

Code :
  1. i=0
  2. lg=len(tab)
  3. for j in range(lg):
  4.    if tab[i] != ce que je veux:
  5.        del tab[i]
  6.    else
  7.        i+=1


???


T'arriveras sûrement à le faire marcher, mais c'est immonde et c'est pas comme ça que code un utilisateur de python [:spamafote]


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
n°1624326
Sve@r
Posté le 16-10-2007 à 07:44:28  profilanswer
 

masklinn a écrit :


Mais de quoi tu parles [:mlc]


Ben le tableau "out" est bien un second tableau qui contient les éléments de "tab" dont je ne veux pas (en fait, dans ton cas il contient les éléments que je veux mais c'est un détail).
Donc si je fais un "for c in out : tab.remove(c)" cela supprimera bien de "tab" les éléments que je voulais supprimer ? En tout cas c'est comme ça que j'ai vu ton exemple.
 

masklinn a écrit :


T'arriveras sûrement à le faire marcher, mais c'est immonde


Reçu. Je ferai pas comme ça.
 

masklinn a écrit :

et c'est pas comme ça que code un utilisateur de python [:spamafote]


Ah ben jdébute et j'ai pas encore toutes les subtilités en tête  :sol:  
 


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
n°1624330
masklinn
í dag viðrar vel til loftárása
Posté le 16-10-2007 à 08:05:04  profilanswer
 

Sve@r a écrit :


Ben le tableau "out" est bien un second tableau qui contient les éléments de "tab" dont je ne veux pas (en fait, dans ton cas il contient les éléments que je veux mais c'est un détail).
Donc si je fais un "for c in out : tab.remove(c)" cela supprimera bien de "tab" les éléments que je voulais supprimer ? En tout cas c'est comme ça que j'ai vu ton exemple.


Heuu oui, mais vu que out contient les éléments que tu veux, tu peux surtout utiliser out directement tu sais [:pingouino]


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
n°1625054
Sve@r
Posté le 16-10-2007 à 23:32:40  profilanswer
 

masklinn a écrit :


Heuu oui, mais vu que out contient les éléments que tu veux, tu peux surtout utiliser out directement tu sais [:pingouino]


Ben c'est un poil plus compliqué que ça. En fait, je suis en train de découvrir PyQt. Donc j'ai fait pour tester une appli PyQT avec un bouton qui lance une sous-fenêtre.

Code :
  1. #!/usr/bin/env python
  2. # coding: Latin-1 -*-
  3. import sys
  4. from PyQt4.QtCore import *
  5. from PyQt4.QtGui import *
  6.  
  7. class QtAppli(QApplication):
  8.     "Fenêtre de l'application"
  9.  
  10.     # Constructeur fenêtre
  11.     def __init__(self,
  12.             argv):
  13.  
  14.         # Appel constructeur de l'objet hértié
  15.         QApplication.__init__(self, argv)
  16.  
  17.         # Attributs de l'application
  18.         self.argv=argv
  19.  
  20.         # Widget principale
  21.         self.wid=QMainWindow()
  22.         self.wid.setCentralWidget(QWidget(self.wid))
  23.         self.wid.statusBar()
  24.  
  25.         # Titre
  26.         self.wid.setWindowTitle("Test QT" )
  27.  
  28.         # Un espace de rangement
  29.         box=QVBoxLayout(self.wid.centralWidget())
  30.  
  31.         # Le bouton
  32.         btn=QPushButton(self.wid.centralWidget())
  33.         btn.setText("Lancement sous-fenêtre" )
  34.         self.connect(btn, SIGNAL("clicked()" ), self.slotAction)
  35.         box.addWidget(btn)
  36.  
  37.         # Pour quitter
  38.         quit=QPushButton(self.wid.centralWidget())
  39.         quit.setText("Quitter" )
  40.         self.connect(quit, SIGNAL("clicked()" ), self, SLOT("quit()" ))
  41.         box.addWidget(quit)
  42.  
  43.     # Affichage et lancement application
  44.     def run(self):
  45.         self.wid.show()
  46.         self.exec_()
  47.  
  48.     # Slot qui affiche une fenêtre avec un texte
  49.     def slotAction(self):
  50.         print "clicked"
  51.  
  52.         dial=QtSub(self.wid.centralWidget())
  53.         dial.show()
  54.  
  55. class QtSub(QDialog):
  56.     "Sous-fenêtre"
  57.  
  58.     # Constructeur fenêtre
  59.     def __init__(self, parent):
  60.  
  61.         # Appel constructeur de l'objet hértié
  62.         QDialog.__init__(self, parent)
  63.  
  64.         self.setWindowTitle("Sous-fenêtre" )
  65.  
  66.         layout=QVBoxLayout(self)
  67.  
  68.         lab=QLabel("<center><font size='+5'>Clicked !!!</font></center>" )
  69.         layout.addWidget(lab)
  70.  
  71.         btn=QPushButton(self)
  72.         btn.setText("Fermer" )
  73.         btn.connect(btn, SIGNAL("clicked()" ), self, SLOT("close()" ))
  74.         layout.addWidget(btn)
  75.  
  76. Appli=QtAppli(sys.argv)
  77. Appli.run()


 
Ca marche bien... sauf que la sous-fenêtre, quoi qu'on fasse, reste toujours affichée au-dessus de la fenêtre principale.
 
J'en ai conclu que c'était peut-être parce que j'appelais la sous-fenêtre en lui passant la fenêtre principale comme parent. J'ai donc corrigé ça et mis "None" comme parent

Code :
  1. # Slot qui affiche une fenêtre avec un texte
  2.     def slotAction(self):
  3.         print "clicked"
  4.  
  5.         dial=QtSub(None)
  6.         dial.show()


 
Là, la fenêtre apparaît puis disparaît très vite. J'en ai conclu que la variable "dial" était libérée par le gc d'où la modif suivante

Code :
  1. # Slot qui affiche une fenêtre avec un texte
  2.     def slotAction(self):
  3.         print "clicked"
  4.  
  5.         self.dial=QtSub(None)
  6.         self.dial.show()


 
Là, c'est presque parfait sauf que si je lance deux ou plusieurs sous-fenêtres, la nouvelle remplace l'ancienne. Donc j'en suis venu à passer par un tableau (initialisé dans le constructeur)

Code :
  1. # Slot qui affiche une fenêtre avec un texte
  2.     def slotAction(self):
  3.         print "clicked"
  4.  
  5.         self.tabDial.append(QtSub(None))
  6.         self.tabDial[-1].show()


 
Là, tout est ok. Sauf que si l'utilisateur ferme une des "n" sous-fenêtres ouvertes, ben elles sont juste fermées mais restent toujours allouées d'où mon idée, dans le slotAction() de commencer par balayer le tabDial et si sa propriété "setVisible()" est à False, cela signifie que la fenêtre a été soit fermée par le bouton soit par la croix et donc de supprimer ces fenêtres devenues inutiles via un tabDial.remove(). ce qui m'a amené au post initial de ce topic (et qui recroise aussi le topic où je cherchais à comprende le gc)...
 
 
 
 


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
n°1627734
multani
Dépressionnisé
Posté le 19-10-2007 à 21:35:08  profilanswer
 

Si les solutions de Masklinn conviennent pas pour ton problème, tu peux aussi passer par deux boucles :

Code :
  1. values_to_remove = [value for value in self.tabDial if not value.setVisible()]
  2.  
  3. for value in values_to_remove:
  4.    self.tabDial.remove(value)


... en gros.
 
Arpsè, j'y connais rien en PyQt, mais ton problème à l'air un peu bizarre quand même. Ya peut-être un problème ailleurs (mais bon, j'y connais rien [:cosmoschtroumpf] )

n°1628076
Sve@r
Posté le 20-10-2007 à 22:09:28  profilanswer
 

multani a écrit :

Si les solutions de Masklinn conviennent pas pour ton problème, tu peux aussi passer par deux boucles :

Code :
  1. values_to_remove = [value for value in self.tabDial if not value.setVisible()]
  2.  
  3. for value in values_to_remove:
  4.    self.tabDial.remove(value)


... en gros.


Oui, c'est ce que j'avais dit dans mon 2° post. Créer un 2° tableau contenant les valeurs à supprimer puis traiter ces valeurs via le premier tableau...
 

multani a écrit :

Après, j'y connais rien en PyQt, mais ton problème à l'air un peu bizarre quand même. Ya peut-être un problème ailleurs (mais bon, j'y connais rien [:cosmoschtroumpf] )


Ben moi aussi je débute avec Qt donc je tatonne un peu...


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.

Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  Python

  Comportement d'une boucle sur un tableau modifié dans la boucle

 

Sujets relatifs
Colonnes de même hauteur avec bordures : possible sans tableau ?Thread, boucle + timer ?
[CSS] Decentrage du contenu d'un tableau [résolu]Génération dynamique de lignes sur un tableau
tableau avec rendu type "figer les volets" ExcelInsertion de tableau dans une richtextbox
[Résolu] Tableau dynamiquetrier un tableau multidimensionnel en fonction d'une colonne
vérifier si une valeur existe bien dans un tableau[SQL] parametre en entrée et boucle sur les requêtes
Plus de sujets relatifs à : Comportement d'une boucle sur un tableau modifié dans la boucle


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR