Forum Programmation.python Besoin d'avis sur algo Python

Posté par  . Licence CC By‑SA.
Étiquettes :
2
20
mar.
2013

J'écris beaucoup de petits programmes en Python mais le code produit n'est pas très propre / efficace. Par exemple, sur ce petit programme, j'aimerais avoir vos avis sur la manière de l'optimiser et de rendre le code plus propre.

#!/usr/bin/env python3
# rot13.py - ROT13 encoder/decoder written in Python.

from sys import argv
from os.path import basename

def rot13(txt):

    """Encoder / décoder la chaine txt."""

    A2Z = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    MAX = len(A2Z)
    MOY = MAX // 2
    res, tmp = str(), str()
    pos = int()

    for car in txt:
        if car.islower():
            tmp = car.capitalize()
        else:
            tmp = car
        if A2Z.count(tmp):
            pos = A2Z.index(tmp)
            pos += MOY
            if pos >= MAX:
                pos -= MAX
            tmp = A2Z[pos]
        if car.islower():
            tmp = tmp.lower()
        res += tmp

    return(res)


if len(argv) > 1:
    for arg in argv[1:len(argv)]:
        print(rot13(arg))
else:
    print("Usage: {0} <chaine1> <chaine2>".format(basename(argv[0])))

Merci d'avance pour vos conseils !

  • # Quelques pistes

    Posté par  (site web personnel) . Évalué à 6.

    Lire le code des autres est très formateur :

    http://stackoverflow.com/questions/3269686/short-rot13-function

    Il serait intéressant de pour toi de comparer le temps d'exécution de ta version avec ceux de ces versions. On y trouve des choses intéressantes en terme de concision et de performances (même si je serais incapable de dire laquelle est la plus rapide) :

    • utilisation de fonctions dédiées (encode() et translate())
    • utilisation de dictionnaires pour faire des recherches rapides, une fois construits (complexité algorithmique en O(1)). La méthode get() est souvent oubliée par les débutants qui vont faire un in (voire pire un has_key()) puis un [key], donc 2 recherches au lieu d'une.
    • utilisation de join() plutôt que += pour concaténer les chaînes.

    Sinon sur ton code en particulier, à part la boucle for qu'il faut revoir, j'aurais des petites trucs en plus :

    • '' est plus rapide que str() (dans le 2ème cas l'interpréteur doit faire une recherche du symbole "str", qui peut être surchargé). La différence est évidemment infime, mais c'est toujours bon à savoir.
    • 0 est plus rapide que int() (même raison).
    • (je pourrais du coup dire la même chose de list() vs [] ou dict() vs {})
    • res, tmp = str(), str() => c'est plus efficace (et plus lisible je trouve) de le faire en 2 lignes, car là l'interpréteur doit construire un tuple temporaire en plus et faire l'unpacking. L'unpacking c'est génial dans bien des cas mais pas là.

    Amuses toi bien :)

    • [^] # Re: Quelques pistes

      Posté par  . Évalué à 2.

      Merci pour tes explications qui sont intéressantes. Je vais mettre ça en application !

  • # j'aime bien le titre "avis sur un algo python"

    Posté par  . Évalué à 1.

    par définition un algo est adaptable quelque soit le langage ;) dans les limites des spécificités de celui ci.
    donc soit l'algo est bon et fonctionne dans tous les langages d'une même famille/d'un meme type ou style
    soit il ne marche pas.

    L'optimisation du code…ça c'est autre chose :D

  • # Essai

    Posté par  . Évalué à 4.

    Je suis loin d'être un pro niveau optimisation, je suis un peu dans le même cas que toi, j'ai juste essayé de diminuer le nombre de lignes. Aucune idée si ça a une influence sur la rapidité d'exécution.

    #!/usr/bin/env python3
    # rot13.py - ROT13 encoder/decoder written in Python.
    
    from sys import argv
    from os.path import basename
    
    def rot13(txt):
    
        """Encoder / decoder la chaine txt."""
    
        A2Z = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        MAX = len(A2Z)
        MOY = MAX // 2 - 1
        res = "" # Pas besoin d'initialiser tmp ou pos tu es en python !
    
        for car in txt:
            tmp = car.capitalize() # Pas besoin de tester si c'est une capitale
            if A2Z.count(tmp):
                pos = A2Z.index(tmp) - MOY # Tu pourrais te passer de la variable pos
                tmp = A2Z[pos] # En python si tu appelles un index negatif dans une liste tu obtiens un element en partant de la fin de la liste
            if car.islower():
                tmp = tmp.lower()
            res += tmp
    
        return(res)
    
    if len(argv) > 1:
        for arg in argv[1:len(argv)]:
            print(rot13(arg))
    else:
        print("Usage: {0} <chaine1> <chaine2>".format(basename(argv[0])))
    
    

    sinon une version plus bourrin en une ligne (list comprehension), je peux détailler si tu le souhaites :

    #!/usr/bin/env python3
    # rot13.py - ROT13 encoder/decoder written in Python.
    
    from sys import argv
    from os.path import basename
    
    def rot13(txt):
    
        """Encoder / decoder la chaine txt."""
    
        A2Z = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz"
        MOY = len(A2Z) // 2
        res = ''.join([ A2Z[A2Z.index(car) - MOY] if A2Z.count(car) else car for car in txt ])
        return(res)
    
    if len(argv) > 1:
        for arg in argv[1:len(argv)]:
            print(rot13(arg))
    else:
        print("Usage: {0} <chaine1> <chaine2>".format(basename(argv[0])))
    
    
    • [^] # Re: Essai

      Posté par  . Évalué à 6.

      Bon, j'ai un peu de temps alors je décompose :

      res = ''.join([ A2Z[A2Z.index(car) - MOY] if A2Z.count(car) else car for car in txt ])
      
      

      1 - La base est une list comprehension, outil très puissant que j'utilise très souvent en lieu et place des boucles for qui permet de construire une liste à partir d'une (ou plusieurs) autre liste :

      a = [ fonction(x) for x in liste ]
      
      

      est équivalent à :

      a = []
      for x in liste:
          a.append(fonction(x))
      
      

      2 - A cette list comprehension j'y ai ajouté un ternary operator afin d'ajouter une condition if

      a = [ fonction(x) if (x>0) else fonction(-x) for x in liste ]
      
      

      est équivalent à :

      a = []
      for x in liste:
          if x > 0:
              a.append(fonction(x))
          else:
              a.append(fonction(-x))
      
      

      3 - la fonction appliquée

      A partir du code simplifié, la fonction est assez simple, j'ai simplement supprimé la variable intermédiaire pos.

      Donc chaque caractère inclus dans la liste se voient appliquer cette "fonction" qui renvoie le caractère (majuscule ou minuscule) converti par rot13 (grâce à la liste initiale modifiée pour ajouter les minuscules) :

      A2Z[A2Z.index(car) - MOY]
      
      

      4 - join

      ''.join() doit être appliqué à la liste car la sortie de la list comprenhsion est une liste de caractères, et non une chaine.

    • [^] # Re: Essai

      Posté par  (site web personnel) . Évalué à 1.

      à la place de

      if A2Z.count(tmp):
      
      

      je ferais un

      if tmp in A2Z:
      
      
  • # gagner du temps

    Posté par  . Évalué à 2.

    pourquoi tester chaque caractere pour le mettre en majuscule si necessaire
    il doit bien y avoir une fonction qui met tout en majuscule en une seule fois.

    tu n'as alors plus qu'a faire tes rot13 sur les caracteres de la chaine, sans avoir à les tester un par un.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.