IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Mon code est-il pythonique


Sujet :

Python

  1. #1
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 215
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 215
    Par défaut Mon code est-il pythonique
    Je m'initie en ce moment à des langages nouveaux ( nouveaux pour moi ).
    Sur le forum algorithme, Petitours a posé une question ici ; le sujet m'a intéressé, et j'ai développé une solution en Python. A priori le code ci-dessous est correct.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    # -*- coding:Utf-8 -*-
     
    # Enoncé de l'exercice : Forum développer.com, message posté par petitours:
    # http://www.developpez.net/forums/d1600384-2/general-developpement/algorithme-mathematiques/general-algorithmique/regroupements-d-objets-adresse/
    #
     
     
    def ff_lire_data(fic ) :
        " Lit le fichier de data,       ligne 1 = timeout,       lignes suivantes = pour chaque paquet, 2 colonnes (adesse,longueur)"
     
        nf = open(fic, 'r')
        #  Traitement des erreurs ...
        lig = nf.readline()
        xx = lig.split(',')
        timeout = int( xx[0] )
     
        ok = 1
        i = 0
        les_addd= []       # Les adresses de tous les paquets
        les_lggg = []      # et leur longueur
        lig = nf.readline()
        while ok == 1  :
            i=i+1
            xx = lig.split(',')
            les_addd.append(  int(xx[0])  )     # Adresse
            les_lggg.append(  int(xx[1])  )     # Longueur
            lig = nf.readline()       
            if lig == "" :
                print (' fin de fichier ')
                ok = 0
     
        nf.close
        return ( [ timeout , i , les_addd, les_lggg ] )         # Timeout, nbre de paquets, plus 2 tableaux addr et longueurs.
    # **********************************************************************************************************
     
     
    # **********************************************************************************************************
    def ff_calcule_cout( add_debut, add_fin, qtimeout) :
        " Calcul du coût de transmission d'un groupe de données. ---------------------------------------"
        ii1 = add_fin - add_debut +1
        ii1 = 12 + 1.04 * ( 5+ ii1 )
        ii2 = 12 + qtimeout
        if ii2 > ii1 :
            ii1 = ii2           # le max des 2 calculs. ------------------------------------------------
        return ii1
    # **********************************************************************************************************
     
     
     
    # **********************************************************************************************************
    def ff_duplique  ( tb ) :
        "Fonction pour dupliquer un tableau" 
        # petite fonction , car quand on fait   tb_svg = tb, ça ne duplique pas les données...
        # J'imagine qu'il existe une fct standard, mais je ne la connais pas, donc je la crée.
        tb2 = []
        for un in tb :
            tb2.append(un)
        return tb2
    # **********************************************************************************************************
     
     
     
     
    # **********************************************************************************************************
    def affiche_en_clair(qscenar, tb_add, tb_lg) :
        "Fonction qui va afficher en clair les endroits où il faut séparer les paquets "
        les_0_1 = qscenar[0]
        print ( "$$$$$ $$$$$ $$$$$ Syntèse scénario $$$$$ $$$$$ $$$$$")
        for i in range(len(les_0_1) ) :
            if les_0_1[i] == 1 :
                print ( " Coupure après le paquet qui débute à l'adresse ", str( tb_add[i] ) , sep = " : " )
     
        for i in range(len(les_0_1) ) :
            if les_0_1[i] == 1 :
                print (  str( tb_add[i] ) , end = " // " )
    # **********************************************************************************************************
     
     
     
     
    # Programme principal   ************************************************************************************
    # 1. Initialisation des données --------------------------------------------------------------------
    [ timeout , nb_paquets, les_addd, les_lggg ] = ff_lire_data( "c:\prj\petitours.csv" )
     
    lg_max = 250
    nb_paquets = nb_paquets+1
    les_addd.append ( 99999999)
    les_lggg.append ( 0)
        # J'ajoute un paquet bidon très loin, ainsi on va s'obliger à 'fermer' après le dernier paquet, dans la boucle générale.(pas besoin de code particulier pour la fin de tableau).
     
    tb_scenar = []
    un_scenar = ( [], les_addd[0], 0  ,0 )  #  1=liste des 0 ou 1  , 2= addresse du 1er paquet après le dernier mur,    3=Cout cumulé    4= Redondant avec 2 , valeur du dernier 0/1
    tb_scenar.append(un_scenar )
     
    # 2. Boucle sur les paquets --------------------------------------------------------------------------
    for i in range( nb_paquets-1) :
        tb_scenar2 = []
        fin_paquet_a_venir = les_addd[i+1] + les_lggg[i+1] - 1
        best_cout = 9999999999
        for un_scenar  in   tb_scenar  :
            les_0_1_svg = un_scenar[0]
            addr_prem_paquet = un_scenar[1]
            cout_cumule = un_scenar[2]
     
            #if fin_paquet_a_venir - addr_prem_paquet <= lg_max :
            for k in range ( 2) :
                # on ajoute 2 scenarios
     
                # k=0 : pas de mur après i,         k=1 : avec un mur après i
                les_0_1 = ff_duplique ( les_0_1_svg)
                les_0_1.append(k)
                if k == 0 :
                    if fin_paquet_a_venir - addr_prem_paquet <= lg_max :
                        # pas de mur après i, ossible uniquement si i+1 n'est pas trop loin.
                        un_scenar = ( les_0_1, addr_prem_paquet, cout_cumule, k )
                        tb_scenar2.append(un_scenar)
                    # Fin du test : if futur-paquet 'compatible
                else :
                    cout_incremental = ff_calcule_cout (  addr_prem_paquet , fin_paquet_a_venir , timeout )
                    nv_cout =  cout_cumule + cout_incremental
                    un_scenar = ( les_0_1, les_addd[i+1],  nv_cout , k )
                    if nv_cout < best_cout :
                        best_cout = nv_cout      # Je mémorise le coût minimal parmi les scénarios qui finnissent par un mur à cet endroit.
                    tb_scenar2.append(un_scenar)
     
            # fin de la boucle for k in 0,1
        # Fin de la boucle : Pour tous les scénarios qui étaient en attente
     
        # Suppression de tous les scénarios 'périmés'
        nj = len(tb_scenar) 
        for j in  range(nj) :
            del ( tb_scenar[0] )
     
        # Nettoyage des scenarios trop couteux,    et on re-transfère vers tb_scenar au lieu de tb_scenar2
        for un_scenar  in   tb_scenar2  :
            if un_scenar[3] == 0 :          # Les scenar  'ouverts'
                tb_scenar.append(un_scenar)
            else :                          # Les scénar fermés par un mur.
                cout_cumule = un_scenar[2]
                if cout_cumule == best_cout :
                    tb_scenar.append(un_scenar)
                    best_cout = -1         #  Je viens de copier un scénario, les autres scénarios ex-aequo ne m'intéressent plus.
     
        print ( "Fin du step i= ", str( i ), "  Nb scénar dans le tuyau = ", str( len( tb_scenar) ), "   Addr/lg du paquet à venir = ", str(les_addd[i+1]), " ; " , str ( les_lggg[i+1] ) )
     
    # Fin de la boucle sur i
    # Affichage du résultat
    for un_scenar in tb_scenar :
        affiche_en_clair ( un_scenar, les_addd, les_lggg )
    Là où je ne suis pas satisfait, c'est que j'ai l'impression d'avoir codé cela comme je l'aurais fait en VB ou je ne sais quel autre langage. Si je poste ce code ici, c'est pour avoir vos retours. Comment mieux utiliser les avantages de Python.Comment rendre ce code 'Pythonique'.
    Je ne vous demande pas de tout refaire de A à Z, ça ne m'aiderait pas, je ne comprendrais probablement rien. Mais si vous pouviez corriger tel ou tel détail, je suis preneur.

  2. #2
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    salut,

    je te propose le code suivant, modifié (et néanmoins non-testé) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    # -*- coding: utf-8 -*-
     
    def ff_lire_data (fic):
       """
       Lit le fichier de data
       ligne 1 = timeout
       lignes suivantes = pour chaque paquet, 2 colonnes (adesse,longueur)
       """
       with open(fic, 'r') as nf:
          timeout = nf.readline().split(',')[0]
     
          les_addd = [] # Les adresses de tous les paquets
          les_lggg = [] # et leur longueur
          for i, lig in enumerate(f):
             addr,longueur = lig.split(',')
             les_addd.append(addr)      # Adresse
             les_lggg.append(longueur)  # Longueur
          print ('fin de fichier')
       return [ timeout , i+1 , les_addd, les_lggg ]  # Timeout, nbre de paquets, plus 2 tableaux addr et longueurs.
     
    def ff_calcule_cout (add_debut, add_fin, qtimeout):
       """
       Calcul du coût de transmission d'un groupe de données.
       """
       ii1 = add_fin - add_debut + 1
       ii1 = 12 + 1.04 * ( 5 + ii1 )
       ii2 = 12 + qtimeout
       return max(ii1, ii2)
     
    ff_duplique = lambda tb: list(tb)
     
    def affiche_en_clair (qscenar, tb_add, tb_lg):
       """
       Fonction qui va afficher en clair les endroits où il faut séparer les paquets
       """
       les_0_1 = qscenar[0]
       print ("$$$$$ $$$$$ $$$$$ Syntèse scénario $$$$$ $$$$$ $$$$$")
       for i in range(len(les_0_1) ) :
          if les_0_1[i] == 1 :
             print (" Coupure après le paquet qui débute à l'adresse : {}".format(tb_add[i]))
       for i in range(len(les_0_1) ) :
          if les_0_1[i] == 1 :
             print (str(tb_add[i]), end = " // ")
     
    if __name__ == '__main__':
       [ timeout, nb_paquets, les_addd, les_lggg ] = ff_lire_data("c:\prj\petitours.csv")
     
       lg_max = 250
       nb_paquets = nb_paquets + 1
       les_addd.append (99999999)
       les_lggg.append (0)
     
       tb_scenar = [(    # initialisation directe d'une liste avec 1 seul tuple
          [],            # liste des 0 ou 1
          les_addd[0],   # addresse du 1er paquet après le dernier mur
          0,             # Cout cumulé
          0              # Redondant avec 2, valeur du dernier 0/1
       )]
     
       for i in range(1, nb_paquets):
          tb_scenar2 = []
          fin_paquet_a_venir = les_addd[i] + les_lggg[i] - 1
          best_cout = 9999999999
          for un_scenar in tb_scenar:
             les_0_1_svg, addr_prem_paquet, cout_cumule, _ = un_scenar
             for k in [0, 1]:  # tant qu'à faire...
                les_0_1 = ff_duplique(les_0_1_svg)
                les_0_1.append(k)
                if k == 0 :
                   if fin_paquet_a_venir - addr_prem_paquet <= lg_max :
                      un_scenar = (les_0_1, addr_prem_paquet, cout_cumule, k)
                      tb_scenar2.append(un_scenar)
                else :
                   cout_incremental = ff_calcule_cout (addr_prem_paquet, fin_paquet_a_venir, timeout)
                   nv_cout = cout_cumule + cout_incremental
                   un_scenar = (les_0_1, les_addd[i], nv_cout, k)
                   best_cout = min(best_cout, nv_cout)
                   tb_scenar2.append(un_scenar)
     
          del tb_scenar[:]  # empty
     
          for un_scenar in tb_scenar2:
             if un_scenar[3] == 0:
                tb_scenar.append(un_scenar)
             else :
                cout_cumule = un_scenar[2]
                if cout_cumule == best_cout :
                   tb_scenar.append(un_scenar)
                   best_cout = -1
     
          print ("Fin du step i= {}   Nb scénar dans le tuyau= {}   Addr/lg du paquet à venir= {} ; ".format(i, len(tb_scenar), les_addd[i+1], les_lggg[i+1]))
     
       for un_scenar in tb_scenar :
          affiche_en_clair (un_scenar, les_addd, les_lggg)
    il ne s'agit ici que de quelques remarques d'ordre général du point de vue de la syntaxe, nullement sur l'optimisation ou l'algorithme en lui-même étant donné que je n'ai absolument pas compris ce dont il était question dans la discussion avec petitours

    • la fonction ff_lire_data() est la plus à même d'être "pythonisée"
    • la fin du bloc with open() as marque implicitement la fermeture du fichier, that's so pythonic...
    • pour récupérer le timeout on lit la première ligne, on découpe sur la virgule et on conserve la première partie : timeout = nf.readline().split(',')[0]
    • la fonction enumerate() renvoit à la fois le numéro de la ligne (à partir de 0) et la ligne elle même, plus besoin de tenir un compteur à jour
    • on hésitera pas à utiliser les fonctions max() et min() plutot que de les déterminer à l'ancienne
    • le fonction ff_duplique() se résume à un return list(tb), s'il faut vraiment faire une fonction on peut la définir comme suit : ff_duplique = lambda tb: list(tb)
    • initialiser un_scenar pour ensuite le rajouter à la liste nouvellement créée tb_scenar n'est pas pertinent, on peut réaliser une initialisation plus directe
    • faire un for i in range(nb-1) pour ensuite utiliser partout des i+1 n'est pas très habile, on peut faire une boucle for i in range(1, nb) directement
    • s'il s'agit de vider tous les elements de tb_scenar tout en conservant son type liste, la syntaxe suivante est plus directe : del tb_scenar[:] ou encore tb_scenar[:] = []
      il semble qu'avec python >= 3.3 la syntaxe tb_scenar.clear() marche aussi
    • l'utilisation de format() dans les chaines à afficher permet de rendre les print() moins longs et plus clairs
    • pour tout le reste il y a PEP8

  3. #3
    Membre émérite
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Septembre 2013
    Messages
    485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2013
    Messages : 485
    Par défaut
    J'ai commencé à regarder ce code et j'ai vite arrêté en faite.
    Bravo à BufferBob qui a eu se courage.

    Alors ma remarque n'est pas du tout sur le "pythonique" mais sur des bonnes partiques de codage.
    Je te rassure, tu n'es pas le seul à avoir ces mauvaises habitudes et tu n'es pas le dernier à qui je fais ces remarques sur ce forum.

    Je m'explique.
    Est-il nécessaire de chercher a nommer les variables avec un nom le plus abstrait possible pour maximiser le risque de ne pas comprendre à quoi elle correspond ?

    En gros, peux-tu, rapidement, sans relire ton code, me donner le rôle et le sens exacte de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    i
    ii1
    ii2
    j
    k
    les_0_1
    les_addd
    les_lggg
    lg_max
    lig
    nf
    nj
    ok
    tb2
    tb_scenar
    tb_scenar2
    un
    un_scenar
    xx
    Pour information, la taille des noms de variables en python n'est pas limité, la norme PEP8 recommande un maximum de 79 caractères
    Donc, ne pas hésiter à avoir des noms bien explicite.
    Personnellement, je bannis tout les noms de moins de 4 caractères.
    Cela évite non seulement de rajouter un roman de commentaire et en plus cela réduit de la crispation des personnes qui vous relisent.

    Je recommande d'avoir cette adage dans la tête quand on développe du code:
    Développer en imaginant que la personne qui va maintenir votre code est un psychopathe meurtrier qui connait votre adresse .

  4. #4
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2013
    Messages
    156
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Santé

    Informations forums :
    Inscription : Octobre 2013
    Messages : 156
    Par défaut
    Citation Envoyé par Laurent 1973 Voir le message

    Pour information, la taille des noms de variables en python n'est pas limité, la norme PEP8 recommande un maximum de 79 caractères

    :
    Je chipote, mais je suis d'accord avec toi sur le fait que les noms des variables sont importants. Par contre, 79 c'est la taille maximum d'une ligne d'après PEP8

  5. #5
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 215
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 215
    Par défaut
    Merci pour vos retours, spécialement BufferBob. Je vais regarder tout cela ce soir et probablement revenir vers vous.

    A Laurent, sans regarde mon code, voici des explications sur mes noms de variables :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    i                itérateur
    ii1 ii2          je ne sais plus , probablement des itérateurs, mais forcément ces 2 variables jouent des rôles symétriques...
    j                 itérateur
    k                itérateur , compteur ?
    les_0_1       tableau de booléens , central dans mon algorithme 
    les_addd     tableau donnant les adresses des paquets à traiter
    les_lggg      tableau donnant les Longueurs des paquets à traiter   ( idéalement, j'aurais dû faire une structure avec (adresse+longueur), puis un tableau avec cette structure.
    lg_max       longueur Max d'une succession de paquet ( En fait, c'est une constante égale à 250 )
    lig              Dans la procédure de lecture de fichier, c'est une ligne de ce fichier
    nf               nf comme n° de fichier   ( open nf, read nf, close nf)      
    nj               Un nombre ( probablement je vais avoir une iteration for j in range ( nj)  ?  )  
    ok              Un Booléen, probablement pour gérer une boucle 'infinie'
    tb2             Certainement un tableau temporaire.
    tb_scenar    un tableau avec tous les Scénarios à traiter
    tb_scenar2   Idem tb_scenar ... Je copie tb_scenar dans tb_scenar2, puis tb_scenar2 dans tb_scenar
    un               itérateur dans une petite procédure qui ne devrait pas exister, (BufferBob m'a donné la syntaxe pour supprimer cette procédure) , j'itère   for un in tableau  
    un_scenar     Structure de Base, qui représente un scénario. (suite de booléens les_0_1, pour savoir où on découpe, puis coût_cumulé de ce scénario ... )
    xx                ?  Une variable temporaire, certainement créée /initialisée sur une ligne, et relue sur la ligne suivante. puis utilisée nulle part ailleurs dans le code.
    Alors, effectivement, certains de ces noms peuvent être abstraits. Mais par exemple LIG ou NF, dans tous les programmes que je fais, j'utilise cette notation NF pour ouvrir/lire/fermer un fichier, et LIG pour lire un fichier. Ces 2 noms de variables sont donc très concrets pour moi et pour les gens qui travaillent avec moi.

    Mais j'ai bien noté la proposition 'pythonique' de BufferBob :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Remplacer :
     
    lig = nf.readline()
    timeout =  lig.split(',')[0]
     
    par :
     
    timeout = nf.readline().split(',')[0]
    (à aménager, car j'ai besoin de convertir timeout en entier au lieu de string)

  6. #6
    Membre émérite
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Septembre 2013
    Messages
    485
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Septembre 2013
    Messages : 485
    Par défaut
    Citation Envoyé par tbc92 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    i                itérateur
    ii1 ii2          je ne sais plus , probablement des itérateurs, mais forcément ces 2 variables jouent des rôles symétriques...
    j                 itérateur
    k                itérateur , compteur ?
    Mais itérateur de quoi? de patate ou de carotte ? toi-même tu de sais pas de quoi justement.
    C'est une (mauvaise) habitude que des développeurs ont d'utiliser ces lettres (i,j,k,l,...) dans des boucles.
    Au résultat, après quelques des boucles imbriquées ou refactoring, on se mélange allégrement entre les indices qui ne veulent absolument rien dire à part "je suis un itérateur".
    Est-ce si difficile de les remplacer par "patate_iter" et "carotte_iter" ?
    Je ne reviendrais pas sur tes autres variables qui pourrait, dans la même idée, être plus explicite dans leur notation.

    Citation Envoyé par tbc92 Voir le message
    ...
    Ces 2 noms de variables sont donc très concrets pour moi et pour les gens qui travaillent avec moi.
    ...
    Vu que l'on est pas habitué à travailler ensemble, nous ne partageons pas la même norme de codage et d'où ma réticente à me plonger dans ton code.
    Or, là justement, tu demandes à des gens non habitué à travailler avec toi de commenter ton code ... afin de savoir s'il colle a de bonnes "règles" de Python.

    Mes remarques ne sont pas contre toi, tbc92: tu ne suit qu'une habitude du monde de l'informatique.
    C'est malheureusement trop fréquent que des équipes logiciels ne cherchent pas à améliorer la lisibilité du code pour quelqu'un qui n'est pas de sa "tribu".

    Je me permet de t'en faire part d'autant plus qu'entant qu'ingénieur qualité, tu dois être très sensibilisé à améliorer les processus de travail dans l'optique de pérennisation.

  7. #7
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 215
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 215
    Par défaut
    Merci beaucoup BufferBob, j'ai appris/redécouvert plein de choses.

    J'ai pris en compte les différentes suggestions, pas forcément toutes.
    Parfois, ça m'a énervé, parce que c'était des trucs que je me souvenais parfaitement avoir lu dans les tutoriels (vider une liste par exemple). D'ailleurs pour vider une liste, je me suis aperçu que j'avais fait encore plus simple à un endroit : tb_scenario2 = [] ... et il s'avère que ça marche. J'ai donc repris cette syntaxe.
    Je n'ai pas appliqué les print (...format), peut être une prochaine version.

    En lisant le commentaire sur with open()... , je n'avais pas vu la subtilité (le close implicite), mais j'ai compris plus tard
    initialiser un_scenar pour ensuite le rajouter à la liste nouvellement créée tb_scenar n'est pas pertinent, on peut réaliser une initialisation plus directe
    Je n'ai pas appliqué ce conseil, je me dis que l'impact en terme de performance est strictement nul, et j'ai l'impression que je vais perdre en lisibilité.

    J'ai essayé de mettre des noms de variables plus explicites. Et j'ai ajouté plein de commentaires, pour faciliter la compréhension du programme.

    Ca me donne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
     
    # -*- coding:Utf-8 -*-
    # xxxx -x*x- coding:Latin-1 -*-
     
    # Enoncé de l'exercice : Forum développer.com, message posté par petitours.
    #
     
     
    def ff_lire_data(fic ) :
        """
        Lit le fichier de data,
        ligne 1 = timeout,
        lignes suivantes = pour chaque paquet, 2 colonnes (adresse,longueur)
        """
     
        with open(fic, 'r') as nf :
            timeout = int ( nf.readline().split(',')[0]  ) 
     
            les_adresses = []       # Les adresses de tous les paquets
            les_longueurs = []      # et leur longueur
     
            for i, lig in enumerate(nf):
                ch_adresse, ch_longueur, ch_filler = lig.split(',')    # split renvoie des chaines     ; J'ai besoin de filler, car mon fichier contient une VIRGULE à la fin de chaque ligne.
                les_adresses.append( int (ch_adresse))      # Adresse
                les_longueurs.append(int (ch_longueur))  # Longueur
            print ('fin de fichier')
            # close implicite grace au with open()
     
        return ( [ timeout , i1 , les_adresses, les_longueurs ] )         # Timeout, nbre de paquets, plus 2 tableaux addr et longueurs.
    # **********************************************************************************************************
     
     
    # **********************************************************************************************************
    def ff_calcule_cout( adresse_debut, adresse_fin, qtimeout) :
        """
        Calcul du coût de transmission d'un groupe de données. ---------------------------------------
        """
        cout_formule_numero_1 = 12 + 1.04 * ( 5+  adresse_fin - adresse_debut +1 )
        cout_formule_numero_2 = 12 + qtimeout
        return  max ( cout_formule_numero_1, cout_formule_numero_2) 
    # **********************************************************************************************************
     
     
    # **********************************************************************************************************
    def affiche_en_clair(qscenario, tb_adresse, tb_longueur) :
        """
        Fonction qui va afficher en clair les endroits où il faut séparer les paquets
        """
        les_0_1 = qscenario[0]
        print ( "$$$$$ $$$$$ $$$$$ Syntèse scénario $$$$$ $$$$$ $$$$$")
        debut_lot = tb_adresse[0]
        for i in range(len(les_0_1) ) :
            if les_0_1[i] == 1 :
                fin_lot = tb_adresse[i] + tb_longueur[i] - 1
                print ( " Lot à transférer = de l'adresse ",  debut_lot, " à ", fin_lot) 
                debut_lot = tb_adresse[i+1]
    # **********************************************************************************************************
     
     
    # Programme principal   ************************************************************************************
    """
    On a des 'PAQUETS', disons 1000 paquets par exemple. Chaque paquet est défini par une ADRESSE et une LONGUEUR.
    Les (adresse,longueur) des paquets sont initialisés par lecture d'un fichier CSV.
    Dans l'énoncé de départ, les PAQUETS sont des emplacements-mémoires d'un HHT. Et on veut récupérer le contenu de ces emplacements mémoire
    (on peut éventuellement récupérer aussi des emplacements non utiles, on saura gérer)
     
    On peut lancer des instructions à ce HHT, pour lui dire : 'Envoie moi les données entre l'adresse A et l'adresse B
    Le protocole de communication impose B <= A +250 ( on aura une variable longueur_max =250 dans le programme, pour pouvoir éventuellement modifier ce paramètre)
     
    Le coût de tranfert du LOT [A,B]  est donné par une fonction FF_CALCULE_COUT ;
    ce coût dépend bien sûr de B-A, mais aussi d'une autre variable QTIMEOUT, lue dans le fichier d'input. (Première ligne du fichier de description des paquets)  
     
    L'objectif de ce traitement est de chercher comment regrouper les PAQUETS en LOTS, pour minimiser le coût total de transfert des données.
     
    exemple : Si on doit transférer le PAQUET addr=1000, Lg=4 et le paquet addr=1020, lg = 2, on a le choix entre transférer ces 2 paquets en 2 LOTS séparés,
    ou bien transférer le LOT de longueur 22 , commençant à l'adresse =1000.
    Selon qTimeout, c'est une option ou l'autre qui sera la plus avantageuse.
     
    Comme dit plus haut, je recense dans TB_SCENARIO tous mes sénarios (toutes les branches de l'arbre)
     
    Je prends tous mes paquets (ils sont classés sur ADRESSE CROISSANTE dans le fichier CSV) ; et pour chaque paquets, j'ai 2 options :
    le PAQUET en cours sera envoyé dans le même lot que le paquet suivant, ou pas.
    Pour décrire un Scénario de façon non-équivoque, il me suffit donc d'un tableau de booléens :
    0 = le paquet en cours est envoyé dans le même LOT que le paquet suivant
    1 = le paquet en cours n'est pas envoyé dans le même LOT que le paquet suivant.
    Mais pour faciliter les traitements, je mémorise en plus pour chaque scénario quelques variables supplémentaires  (coût cumulé par exemple)
     
    Quand à un moment de mon analyse, j'ai plusieurs scénarios qui tous prévoient de regrouper des LOTS entre le paquet 0 et le paquet N, et de faire une coupure après ce paquet N,
    je peux comparer les coûts de ces scénarios, et garder un seul de ces scénarios, le plus économique.
    Quelles que soient les façons dont on associera les paquets au delà ce ce paquet N, ça ne changera rien au résultat partiel.
     
    Grâce à cet écrémage, le nombre de branches à explorer reste très faible  (250 max   grâce à la limite longueur_max=250, mais en fait beaucoup plus faible)
    Sans cet écrèmage, on aurait 2^^nb_paquets scénarios à analyser : pas réalisable.
    """
     
    if __name__ == '__main__':
        # 1. Initialisation des données --------------------------------------------------------------------
        [ timeout , nb_paquets, les_adresses, les_longueurs ] = ff_lire_data( "c:\prj\petitours.csv" )
        longueur_max = 250
     
        # J'ajoute un paquet bidon très loin, ainsi on va s'obliger à 'fermer' après le dernier paquet, dans la boucle générale.(pas besoin de code particulier pour gérer la fin de tableau).
        nb_paquets = nb_paquets+1
        les_adresses.append ( 99999999)
        les_longueurs.append (0)
     
        tb_scenario = []
        tb_scenario2 = []
        un_scenario = ( [], les_adresses[0], 0  ,0 )
        """
        1=liste des 0 ou 1 = Liste des endroits où on regroupe ou sépare nos paquets.
        2=adresse du 1er paquet apres le dernier mur,
        3=Cout cumulé
        4=Redondant avec 2 , emplacement de la dernière coupeure entre LOTS 
        """
        tb_scenario.append(un_scenario)
     
        # 2. Boucle sur les paquets --------------------------------------------------------------------------
        """
        je ne fais pas à proprement parler du parcours d'arbre, je recense toutes les branches de l'arbre.
        Mais dès que je peux supprimer telle ou telle branche de l'arbre qui s'avère inutile parce que trop coûteuse ou incompatible avec les contraintes, je supprime cette branche.
        Plus précisément, si la branche est incompatible avec les règles, je ne la crée pas dans mon tableau tb_scenario2
        Et si elle est compatible avec les règles mais trop couteuse, je la crée dans scenario2, mais je ne la copie pas dans tb_scenario.
        """
        for numero_paquet  in range( 1, nb_paquets) :
            tb_scenario2 = []                   # ou del tb_scenario2[:]
     
            fin_paquet_a_venir = les_adresses[ numero_paquet] + les_longueurs[numero_paquet] - 1
            best_cout = 9999999999
            for un_scenario  in  tb_scenario :
                les_0_1_svg = list( un_scenario[0] )
                addr_prem_paquet = un_scenario[1]
                cout_cumule = un_scenario[2]
     
                for k in range ( 2) :
                    # on ajoute en principe 2 scenarios  0 ou 1 , PAsede séparation ou Séparation avant le PAQUET_A_VENIR
     
                    # k=0 : pas de mur avant numero_paquet,         k=1 : avec un mur avant numero_paquet
                    les_0_1 = list ( les_0_1_svg)
                    les_0_1.append(k)
                    if k == 0 :
                        if fin_paquet_a_venir - addr_prem_paquet <= longueur_max :
                            # pas de mur avant numero_paquet, possible uniquement si numero_paquet n'est pas trop loin de addr_prem_paquet
                            un_scenario = ( les_0_1, addr_prem_paquet, cout_cumule, k )
                            tb_scenario2.append(un_scenario)
                        # Fin du test : if futur-paquet 'compatible'
                    else :
                        cout_incremental = ff_calcule_cout (  addr_prem_paquet , fin_paquet_a_venir , timeout )
                        nv_cout =  cout_cumule + cout_incremental
                        un_scenario = ( les_0_1, les_adresses[numero_paquet],  nv_cout , k )
                        best_cout = min ( best_cout, nv_cout )              # Je mémorise le coût minimal parmi les scénarios qui finnissent par un mur à cet endroit.
                        tb_scenario2.append(un_scenario)
     
                # fin de la boucle for k in 0,1
            # Fin de la boucle : Pour tous les scénarios qui étaient en attente
     
            # Suppression de tous les sénarios 'périmés'
            tb_scenario = []            # ou del tb_scenario[:]  
     
            # Nettoyage des scenarios trop couteux,    et on re-transfère vers tb_scenario au lieu de tb_scenario2
            for un_scenario  in  tb_scenario2  :
                if un_scenario[3] == 0 :          # Les scenarios  'ouverts' , je les conserve tous sans condition.
                    tb_scenario.append(un_scenario)
                else :                          # Les scénar fermés par un mur, je n'en conserve qu'un : le moins cher.
                    cout_cumule = un_scenario[2]
                    if cout_cumule == best_cout :
                        tb_scenario.append(un_scenario)
                        best_cout = -1         #  Je viens de copier un scénario, les autres scénarios ex-aequo ne m'intéressent plus ; je donne donc une valeur 'farfelue' à best_cout.
     
            print ( "Fin du step i= ",  numero_paquet - 1 , "  Nb scénarios dans le tuyau = ",  len(tb_scenario) , "   Addr/lg du paquet à venir = ", les_adresses[numero_paquet] , " ; " ,  les_longueurs[numero_paquet]  )
     
        # Fin de la boucle sur numero_paquet , et donc fin du traitement.
     
     
        # 3. Affichage du résultat ---------------------------------------------------------------------------------------------------
     
        #Je boucle sur tb_scenario, mais par construction, il ne peut y avoir qu'un scénario dans tb_scenario.
        if len(tb_scenario) != 1 :
            print ( "ERREUR ! Je devrais avoir un seul scénario à ce niveau, et j'en ai : " , len(tb_scenario) ) 
     
        for un_scenario in tb_scenario :
            affiche_en_clair ( un_scenario, les_adresses, les_longueurs)
    Je me doute que pour un projet plus conséquent, ou même pour la lisibilité, il faudrait créer une classe 'PAQUET', et une classe 'SCENARIO' ... peut-être dans une version à venir.

    Je suis bien sûr toujours intéressé par des feed-backs.

  8. #8
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    Citation Envoyé par tbc92 Voir le message
    pour vider une liste, je me suis aperçu que j'avais fait encore plus simple à un endroit : tb_scenario2 = [] ... et il s'avère que ça marche. J'ai donc repris cette syntaxe.
    mais ça n'est justement pas la même chose
    tb_scenario2[:] = [] vide tous les éléments de la liste, alors que tb_scenario2 = [] redéfinis une nouvelle liste vide, mais "l'ancienne" existe toujours en mémoire, elle a juste perdu son étiquette "tb_scenario2"
    dans ton cas ça fonctionne parce que tb_scenario2 est la seule variable qui référence cette liste, donc ce doit être le garbage collector qui finit par poubelliser la vieille liste, la différence entre les deux serait plus flagrante si tu avais par exemple plus haut coincoin = tb_scenario2, la liste coincoin deviendrait alors la seule référence sur l'ancienne liste au lieu de la voir vidée, c'est subtilement retord, dans le doute et sous python 3, préférer tb_scenario2.clear() plus explicite

    Je n'ai pas appliqué ce conseil, je me dis que l'impact en terme de performance est strictement nul, et j'ai l'impression que je vais perdre en lisibilité.
    (...)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
        tb_scenario = []
        un_scenario = ( [], les_adresses[0], 0  ,0 )
        """
        1=liste des 0 ou 1 = Liste des endroits où on regroupe ou sépare nos paquets.
        2=adresse du 1er paquet apres le dernier mur,
        3=Cout cumulé
        4=Redondant avec 2 , emplacement de la dernière coupeure entre LOTS 
        """
        tb_scenario.append(un_scenario)
    c'est toi qui vois, il y a d'une part une variable intermédiaire qui ne sert à rien (utilisée 1 seule fois) et d'autre part la docstring juste en dessous de la définition... tu trouves vraiment ça plus clair ? ^^

    Je me doute que pour un projet plus conséquent, ou même pour la lisibilité, il faudrait créer une classe 'PAQUET', et une classe 'SCENARIO'
    ça n'est pas la même chose, à mon sens il s'agit plutôt de changer de paradigme, là ton code déroule toujours de manière relativement procédurale (modulo la notation pointée ici et là machin.f1().f2()), intégrer des classes change fondamentalement la logique de programmation pour l'orienté objet, la transition entre les deux n'a rien d'évident
    par ailleurs -et par convention- il me semble qu'on choisira de nommer la classe 'MaClassePaquet' ou 'Scenario', les noms tout en majuscules étant plutôt réservés aux variables vouées à être des constantes

  9. #9
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 215
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 215
    Par défaut
    Sur le vidage des éléments de TB_Scenario, je me doutais qu'il y avait une différence. Ok, noté.
    Le fait de modifier le paradigme, en passant en POO, oui. Je l'ai expérimenté déjà sur d'autres langages, je suis bien conscient qu'il y a pas mal de conséquences. Je ne le voyais pas comme une modification du programme existant, mais comme un nouveau programme.

  10. #10
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Salut,

    Juste pour le fun de montrer quelques constructions Python applicables ici....
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    77
    78
    79
    80
    81
    82
    83
    84
    85
    # -*- coding:utf-8 -*-
    from collections import namedtuple
    import csv
     
    HUGE_NUMBER = 9999999
     
    Segment = namedtuple('Segment', 'address size')
    Scenario = namedtuple('Scenario', 'boolmap address total_cost index')
    Table = namedtuple('Table', 'best_cost scenarii')
     
    tx_cost = lambda s, e, tmo: max(12 + 1.04 * ( 5 + (e - s) + 1), 12 + tmo)
     
    # **********************************************************************************************************
    def affiche_en_clair(qscenar, segments) :
        "Fonction qui va afficher en clair les endroits où il faut séparer les paquets "
        les_0_1 = qscenar[0]
        print ( "$$$$$ $$$$$ $$$$$ Syntèse scénario $$$$$ $$$$$ $$$$$")
     
        for ix, boolean in enumerate(les_0_1):
            if boolean :
                print ( "Coupure après le paquet qui débute à l'adresse: %d" % segments[ix].address)
     
        for ix, boolean in enumerate(les_0_1):
            print (segments[ix].address, end='//')
            if boolean == 1 :
                print ()
    # **********************************************************************************************************
     
    def add_segment(table, segment, timeout):
     
        slist = []
        fin_paquet_a_venir = sum(segment) - 1
        best_cost = table.best_cost
     
        for scenario in table.scenarii:
            for k in range (2) :
                boolmap = scenario.boolmap[:] + [k]
                if k == 0 :
                    if fin_paquet_a_venir - scenario.address <= MAX_SIZE :
                        slist.append(Scenario( boolmap, scenario.address, scenario.total_cost, k ))
                else :
                    nv_cout =  scenario.total_cost + tx_cost (scenario.address, fin_paquet_a_venir, timeout )
                    slist.append(Scenario(boolmap, segment.address,  nv_cout , k ))
                    if nv_cout < best_cost :
                        best_cost = nv_cout      # Je mémorise le coût minimal parmi les scénarios qui finnissent par un mur à cet endroit.
     
        return Table(best_cost, slist)
     
    def filter_(table):                           # Nettoyage des scenarios trop couteux
     
        def get_scenarii(best_cost, scenarii):
            for s in table.scenarii:
                if not s.index:                    # Les scenar  'ouverts'
                    yield s
                elif s.total_cost == best_cost:
                    yield s
                    best_cost -= 1  
     
        return Table(HUGE_NUMBER, list(get_scenarii(*table)))
     
     
    def create_table(segments, timeout):
        table = Table(HUGE_NUMBER, [Scenario( [], segments[0].address, 0  ,0 )])    
     
        # 2. Boucle sur les paquets --------------------------------------------------------------------------
        for i, segment in enumerate(segments[1:]):
    #         table = add_segment(table, segment)
    #         table = filter_(table)        
            table = filter_(add_segment(table, segment, timeout)) 
            print ( "Fin du step i= ", str( i ), "  Nb scénar dans le tuyau = ", str( len( table.scenarii) ), "   Addr/lg du paquet à venir = ", str(segment.address), " ; " , str ( segment.size ) )
        return table
     
     
    if __name__ == '__main__':
        TIMEOUT = 10   
        MAX_SIZE = 50
     
        with open("petitours.csv") as f:  # à adapter suivant le format du fichier.
            segments = [ Segment(*[ int(z) for z in row ]) for row in csv.reader(f) ]
     
        segments.append(Segment(99999, 0))
        table = create_table(segments, TIMEOUT) 
     
        for un_scenar in table.scenarii :
            affiche_en_clair (un_scenar, segments )
    Comme il l'a déjà été dit, le principal problème du code est son manque de lisibilité.
    Et le manque de lisibilité vient souvent d'un certain laxisme dans le nommage de ses variables: quoi est associé à qui? Python offre l'équivalent d'un struct appelé namedtuple dont j'abuse ici. C'est pratique à utiliser lorsqu'on à des tas de tuples "normaux" car ce sont aussi des tuple...

    Et on peut refactoriser le code par morceau (voir la fonction "affiche_en_clair" qui est a peut près restée dans son jus).

    Un autre truc c'est qu'au moins il y a de variables à nommer, on moins on a de problèmes de nommage.
    Moins de lignes de codes, des appels de fonctions bien placés, l'usage de règles simples comme utiliser les variations de segments(collection), segment(instance), Segment(factory),...

    note: le code est prêt pour faire de Table une vraie "classe". Est-ce qu'on a changé le design du code? Non j'ai juste aminci une boucle immense en créant deux fonctions (add_segment et filter_). x

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  11. #11
    Rédacteur/Modérateur

    Homme Profil pro
    Ingénieur qualité méthodes
    Inscrit en
    Décembre 2013
    Messages
    4 215
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur qualité méthodes
    Secteur : Conseil

    Informations forums :
    Inscription : Décembre 2013
    Messages : 4 215
    Par défaut
    Avant tout merci, je vais décortiquer chaque phrase et chaque ligne de code, j'e sais que je vais apprendre plein de choses.

    J'aime beaucoup le concept de namedtuple. Je ne l'avais pas croisé dans mes pérégrinations, et clairement, ça manquait.

    Par contre, je vais avoir du mal à assimiler des syntaxes comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
         segments = [ Segment(*[ int(z) for z in row ]) for row in csv.reader(f) ]
    En terme de temps de traitement, y-a-t-il un bénéfice sensible à ""piper"" les affectations de cette façon ?

  12. #12
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 741
    Par défaut
    Salut,

    Citation Envoyé par tbc92 Voir le message
    Par contre, je vais avoir du mal à assimiler des syntaxes comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
         segments = [ Segment(*[ int(z) for z in row ]) for row in csv.reader(f) ]
    En terme de temps de traitement, y-a-t-il un bénéfice sensible à ""piper"" les affectations de cette façon ?
    Excepté côté longueur des lignes, je me suis efforcé de vous faire un florilège de constructions "pythonic" sans utiliser les "class". Donc oui, il y a des constructions un peu originales qui sont difficile à comprendre car très "pythoniques".

    Dans cette ligne là, vous avez list comprehension, unpacking, module csv, ...
    Si on écrit çà pépère, çà donnerait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    segments = []
    for row in csv.reader(f):
        nums = []
        for z in row:
            nums.append(int(z))
        s = Segment(nums[0], nums[1])
        segments.append(s)
    C'est plus facile à lire mais c'est la forme list comprehension n'est qu'une habitude que prend rapidement le programmeur Python (et on s'amuse beaucoup ici à poster des codes en une seule ligne pour le fun).

    Côté performance, çà dépendra de ce que çà fait et du nombre d'itération (il faut mesurer avec le module timeit) mais "sur le papier", on évite les appels explicites à la méthode .append (ici, il y en a deux). Les "list compréhension" utilisent une méthode "interne" plus rapide.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Dates] Le résultat de mon code est inexact
    Par bebas dans le forum Langage
    Réponses: 1
    Dernier message: 27/02/2007, 10h50
  2. Réponses: 1
    Dernier message: 08/02/2007, 09h11
  3. Pourquoi mon code est plus lent que Arrays.sort
    Par alexis779 dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 12/12/2006, 12h44
  4. [Tableaux] Mon code est bon ?
    Par garaut dans le forum Langage
    Réponses: 8
    Dernier message: 14/11/2006, 15h47
  5. [Dates] calcul de date est ce que mon code est bon?
    Par carmen256 dans le forum Langage
    Réponses: 2
    Dernier message: 09/06/2006, 11h30

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo