Recuperer les éléments d'une liste

et autre problèmes

a marqué ce sujet comme résolu.

Bonjour,

Je viens d’écrire mon premier code "python" (oui il n’est pas bien, pas optimal, pas pythonique, variables mal choisies..)

Mais je ne comprends pas pourquoi je n’ai qu’un seul élément dans columns. (nombre d’éléments affichés avec print(number) )

J’ai une une première erreur, comme quoi je ne pouvais pas iterer sur un generator (?) du coup j’ai cherché rapidement sur stackoverflow et apparament je devais mettre list() autour. Je précise qu’il n’y a pas dérreur dans mon fichier excel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from random import randint

from openpyxl import load_workbook

wb = load_workbook('fichier1.xlsx')

# grab the active worksheet
number = 0
ws = wb.active
columns = list(ws.iter_cols(min_row=1, max_row=3, max_col=2, min_col=2))
number = len(columns)
print (number)
to_check = []

while len(to_check) < (number/10):
    state = 0
    randn = 0
    while (state == 0) :
        state = 1
        randn = randint(0, number)
        for cel, ind in enumerate(to_check):
            if cel.row == randn:
                state = 0

    for col in columns:
        for cell in col:
            if cell.row == randn:
                to_check.append(cell)
                print(cell.value, end='\n')

PS: J’utilise visual studio code avec une extension python, est-ce que il y a un moyen de lancer le code depuis l’éditeur ? Sans débuggeur ? Pour l’instant je fais clic droit/ lancer depuis un terminal python, ça marche bien, mais est-ce qu’il y a un raccourci ? De plus j’ai beaucoup de trucs soulignés en vert dans mon l’éditeur, et un autre en rouge (relatif à iter_cols) alors que mon code compile. Une idée ?

Merci !

+0 -0

Bonjour,

As-tu tenté d’afficher le contenu de columns pour comprendre le problème ? Je ne connais pas openpyxl, mais il est bien possible que le format des données retournées diffère de ce à quoi tu t’attendais.

Il n’y a bien sûr aucun problème à itérer sur un générateur. Le soucis en voyant ton code c’est que tu tenterais d’itérer plusieurs fois sur un même générateur, ce qui est impossible car le générateur est consommé pendant le parcours.

PS : En lisant tes avertissements sur le code, je m’attendais à bien pire, ce n’est pas si mal :)

Edit : En fait, dans ton iter_cols tu passes min_col=2 et max_col=2, tu n’autorises donc que la seule colonne 2. Ça me paraît alors normal que tu n’aies qu’un élément en retour.

J’ai lu dans la doc que ça renvoie toute la colonne 2, et d’ailleurs je viens de faire un print et on voit bien qu’il contient toutes les cellules que je souhaite avoir.

Qu’est-ce qu’un générateur ?

Je mets ici ce que ça m’affiche:

1
[(<Cell SQL Results.B1>, <Cell SQL Results.B2>, <Cell SQL Results.B3>, <Cell SQL Results.B4>, <Cell SQL Results.B5>, <Cell SQL Results.B6> etc....

J’ai l’impression que [(< me dit que ce n’est pas une vrai liste, et peut être que les élements d’une liste sont délimités par () ?

+0 -0

Oui, donc ça te renvoie une liste de 1 élément, qui est lui-même une liste des cellules de cette colonne (j’utilise ici liste comme un terme générique pour un objet sur lequel on peut itérer).

Les [ et ] sont les symboles pour délimiter les listes, ( et ) délimitent ici un tuple (liste non-modifiable), et les chevrons sont juste la représentation que donne la bibliothèque d’une cellule.

Pour ce qui est des générateurs, je peux te renvoyer ici : https://zestedesavoir.com/tutoriels/954/notions-de-python-avancees/5-generators/

Nickel, merci pour ton aide. J’ai réussi à faire tout ce que je voulais faire. Mon programme marche et fonctionne exactement comme il le faut.

Du coup je ne connais pas trop les bonnes pratiques pythons (outre les noms de variable mals choisis comme var1 et var2), est-ce que vous seriez OK pour critiquer mon code ?

Autre question, j’ai utilisé cx_freeze et je n’arrivais pas à trouver comment faire marcher tkinter avec, du coup j’ai regardé j’ai intégré les paths de TCL et TK dans le script de build et après j’ai mis les DLL dans le dossier. ça fonctionne parfaitement chez moi, mais il n’y a pas moyen de combiner tout les fichiers du dossier en un seul executable ? parce que là c’est 1400 fichiers dans le dossier (rangés dans des sous dossiers), avec parmis eux mon fichier exe., et j’aimerais n’avoir que 1 fichier (le exe), voir les DLL à côté. C’est possible ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from random import randint

from tkinter import * 
from tkinter.filedialog import *

from openpyxl import load_workbook
filename1 = 'pas de fichier'
filename2 = 'pas de fichier'
fenetre = Tk()

var1 = StringVar()
var2 = StringVar()

def callback1():
    global filename1
    filename1 = askopenfilename(title="Ouvrir votre fichier excel",filetypes=[('excel files','.xlsx')])
    var1.set(filename1)

def callback2():
    global filename2
    filename2 = askopenfilename(title="Ouvrir votre fichier excel",filetypes=[('excel files','.xlsx')])
    var2.set(filename2)

def callback3():
    wb = load_workbook(filename1)
    newfile = load_workbook(filename2)
    ws_new = newfile.active
    ws_new['H1'].value = 'CONTROLE'
    ws_new['I1'].value = 'ETAT'

    # grab the active worksheet
    number = 2
    ws = wb.active
    columns = [list(i) for i in list(ws_new.iter_cols(min_row=2, max_col=2, min_col=2))][0]
    number = len(columns)
    to_check = []

    while len(to_check) < (number/10):
        state = 0
        randn = 0
        while (state == 0) :
            state = 1
            randn = randint(0, number)
            for cel in to_check:
                if cel.row == randn:
                    state = 0

        for cell in columns:
            if cell.row == randn:
                to_check.append(cell)
                #print(cell.value, end='\n')

    old_check = [list(i) for i in list(ws.iter_cols(min_row=2, max_col=9, min_col=9))][0]
    for old_cell in old_check:
        if str(old_cell.value).lower() != 'ok' and str(old_cell.value).lower() != 'none':
            print(str(old_cell.value).lower())
            to_check.append(ws['H'+ str(old_cell.row)])

    for i, cell in enumerate(to_check):
        ws_new['H'+str(i+2)].value = cell.value

    wb.save(filename1)
    newfile.save(filename2)

label1 = Label(fenetre, text=filename1, textvariable=var1)
label1.pack()
label2 = Label(fenetre, text=filename2, textvariable=var2)
label2.pack()

Button(fenetre, text ='Premier fichier', command=callback1).pack(side=LEFT, padx=5, pady=5)
Button(fenetre, text ='Second fichier', command=callback2).pack(side=RIGHT, padx=5, pady=5)
Button(fenetre, text ='Generer', command=callback3).pack(side=BOTTOM, padx=5, pady=5)

fenetre.mainloop()

`
+0 -0

Tant mieux si tu as réussi à corriger tes problèmes.

Question bonnes pratiques :

  • Éviter les import * qui encombrent inutilement l’espace de nom courant. Les tutoriels Tkinter procèdent souvent de la sorte, mais un import tkinter as tk serait préférable par exemple ;
  • Éviter aussi les déclarations global, elles peuvent s’avérer utiles dans des cas précis, mais sont dispensables ici ;
  • Éviter à tout prix la répétition de code, les fonctions callback1 et callback2 sont très similaires ;
  • Tu sais que le nom de tes objets n’est pas bon (var1, callback1, etc.), mais ça n’est pas négligeable. Le nom doit décrire la valeur ;
  • En Python on met toujours une espace avec une virgule ;
  • Il est inutile de tout transformer en liste, lors d’un [expr for val in iterable], iterable est déjà un itérable, il n’a pas besoin d’être converti ;
  • Tu peux remplacer [expr for val in iterable][0] par next(expr for val in iterable), ça évite de calculer des valeurs inutiles ;
  • On évite généralement les nombres magiques, ici on trouve des 2, des 5, des 9 et des 10 dans ton code, sans réelle explication ;
  • On ne met pas de parenthèses à l’expression du while, ni d’espace avant les : ;
  • Je ne vois pas trop ce que tu cherches à faire entre les lignes 38 et 50, mais ça semble tout sauf optimal ;
  • Plutôt qu’un val != 'foo' and val != 'bar', on peut préférer le plus concis val not in ('foo', 'bar').

Pour ce qui est de cx_freeze, pourquoi souhaites-tu faire cela ?

Point par point:

  • C’est corrigé. tkinter as tk c’est un alias ?
  • J’avais pas mis de global, mais quand la fonction callback était appellée elle chengeait bien la variable filename dans la variable mais à l’extérieur de la fonction la variable ne changeait pas. j’ai l’impression que filename1 devenait une variable locale au lieu de modifier l’autre variable
  • Du coup vaut mieux faire un callback(1 ou 2) et faire une condition dans la fonction callback ? Ou il y a mieux en python ?
  • OK pour les parenthèses. Mais du coup au niveau des priorités opératoires c’est pareil qu’en C ?
  • Nombres magiques et noms, je suis bien au courant. C’est réglé pour les noms. On peut faire des #DEFINE (équivalent) en python où il y a une meilleur méthode ?
  • virgules, not in: merci !!
  • entre 38 et 50, c’est plutôt un problème d’algo, je devais selectionné au hasard une cellule et regardé que je ne l’avais pas déjà selectionné. (j’étais très fatigué)

Je devais faire ça pour un membre de ma famille. Je me suis dit que c’était l’occasion d’essayer des nouvelles technos (python, vs code…) et ça marchait très bien en console mais par soucis de facilité d’utilisation j’ai voulu faire une GUI, le problème c’est que la personne n’a pas python sur son PC. DU coup je devais trouver un moyen de fournir un exe, je suis tombé sur pyexe et cx_freeze. Il me semble que l’autre ne marche pas avec python 3, du coup j’ai essayé cx_freeze.

EDIT: désolé pour les trop nombreuses fautes de français.

EDIT2: du coup le code modifié (sans les nombres magiques)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from random import randint

import tkinter as tk
from tkinter.filedialog import *

from openpyxl import load_workbook
filename1 = 'pas de fichier'
filename2 = 'pas de fichier'
fenetre = Tk()

label1text = StringVar()
label2text = StringVar()

def selectFile1():
    global filename1
    filename1 = askopenfilename(title="Ouvrir votre fichier excel", filetypes=[('excel files','.xlsx')])
    label1text.set(filename1)

def selectFile2():
    global filename2
    filename2 = askopenfilename(title="Ouvrir votre fichier excel", filetypes=[('excel files','.xlsx')])
    label2text.set(filename2)

def generate():
    wb = load_workbook(filename1)
    newfile = load_workbook(filename2)
    ws_new = newfile.active
    ws_new['H1'].value = 'CONTROLE'
    ws_new['I1'].value = 'ETAT'

    ws = wb.active
    columns = next(list(i) for i in ws_new.iter_cols(min_row=2, max_col=2, min_col=2))
    number = len(columns)
    to_check = []

    while len(to_check) < (number/10):
        state = 0
        randn = 0
        while state == 0:
            state = 1
            randn = randint(0, number)
            for cel in to_check:
                if cel.row == randn:
                    state = 0

        for cell in columns:
            if cell.row == randn:
                to_check.append(cell)
                #print(cell.value, end='\n')

    old_check = next(list(val) for val in ws.iter_cols(min_row=2, max_col=9, min_col=9))

    for old_cell in old_check:
        if str(old_cell.value).lower() not in ('ok', 'none'):
            print(str(old_cell.value).lower())
            to_check.append(ws['H'+ str(old_cell.row)])

    for i, cell in enumerate(to_check):
        ws_new['H'+str(i+2)].value = cell.value

    wb.save(filename1)
    newfile.save(filename2)

label1 = Label(fenetre, text=filename1, textvariable=label1text)
label1.pack()
label2 = Label(fenetre, text=filename2, textvariable=label2text)
label2.pack()

Button(fenetre, text ='Premier fichier', command=selectFile1).pack(side=LEFT, padx=5, pady=5)
Button(fenetre, text ='Second fichier', command=selectFile2).pack(side=RIGHT, padx=5, pady=5)
Button(fenetre, text ='Generer', command=generate).pack(side=BOTTOM, padx=5, pady=5)

fenetre.mainloop()

`
+0 -0
  • Oui, c’est un alias, ça permet donc des expressions plus courtes, mais import tkinter serait tout aussi valable ;
  • C’est en effet le comportement normal, et c’est pour ça qu’intervient le mot-clef global, je pensais plus à une solution avec un dictionnaire contenant les noms de fichiers par exemple ;
  • Avec cette solution du dictionnaire, tu pourrais avoir un unique callback, qui recevrait en paramètre tout ce dont il a besoin, et tu aurais des lambdas lignes 69 et 70 ;
  • Oui, les niveaux de priorités sont les mêmes, mais c’est à ton while (state == 0) que je pensais, où il n’est pas question de priorités ;
  • Tu as encore des problèmes de nomenclature sur les noms, en Python c’est snake_case pour les variables et fonctions. Pas de #define en Python, non ;
  • Je trouve qu’il y a encore pas mal de nombres magiques dans le code.

Si tu veux en savoir plus sur les bonnes pratiques, je peux aussi te renvoyer ici.

Le PC sur lequel tu veux utiliser ton programme n’a pas Python d’installé, ok, mais pourquoi ne pas l’installer ? Je suppose qu’actuellement ton programme n’est pas non plus disponible sur cette machine, pourtant tu vas faire en sorte qu’il le soit.

Tu peux aussi persévérer avec cx_freeze, mais je ne pourrai personnellement pas t’aider, ne m’y étant jamais intéressé.

Je crois que je vais aller lire un peu de doc sur le langage, et ton tuto entwann. Je reviens si j’ai des questions.

Je ne peux pas me permettre d’installer python, parce que c’est un ordinateur professionel et ils n’ont pas les droits pour installer des logiciels. Vous avez des alternatives ? J’en conviens que ça m’a fait **** de donner un zip ave 1400 fichiers pour un petit script de 100 lignes…

+0 -0

Tu dois pouvoir installer Python sur la session utilisateur, dans son dossier perso.

Sinon, si c’est un petit script que tu souhaites distribuer facilement (ça peut paraître radical, mais bon), tu pourrais utiliser un langage compilé (je partirais personnellement vers Go, mais c’est pas du tout objectif comme choix, et je ne sais pas si y’a des libs pour gérer les fichiers excels).

Oulà, il est temps de prendre un peu de recul.

Go, oui c’est un langage bien sympa, dans lequel tu compiles un binaire qui se lance tout seul sur la machine sur laquelle tu installes le soft, mais le coût du développement n’est carrément pas le même que celui d’un petit script en Python.

Dans ton cas précis, perso, même si j’aime bien Go, ça ne me viendrait pas à l’esprit de coder ce script en Go. C’est pas son métier.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte