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 :

Optimisation de boucle


Sujet :

Python

  1. #1
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut Optimisation de boucle
    Bonjour à tous

    Je développe depuis un certain temps en Python et j'ai pris une certaine habitude. Et je viens ici afin d'avoir des conseils savoir si ce que je fais est bien ou pas.

    Cela part au départ d'un objet style toto qui contient une méthode style start()
    Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class toto:
        def start(self):
            ...
    # class toto

    Ensuite, il m'arrive d'avoir non pas une instance mais un tableau d'instances, style
    Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
    tab=[toto(), toto(), toto(), toto(), toto()]

    Et là où je veux en venir, c'est quand je veux appeler toutes les méthodes start. J'ai donc 2 façons de faire

    La première, une boucle classique
    Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for t in tab:
        t.start()

    Et la seconde, passer par une list comprehension
    Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
    [t.start() for t in tab]

    Et moi, je prends de plus en plus l'habitude de passer par la seconde méthode car c'est plus pratique à écrire. Surtout si je dois en plus filtrer certains éléments
    Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    for t in tab:
        if t...:
            t.start()

    Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
    [t.start() for t in tab if t...]

    Mais ce qui me gène c'est que dans la seconde méthode je génère une liste pour rien puisque je ne la récupère pas.

    Et donc je venais ici afin d'avoir vos avis sur les deux façons de faire et la meilleure à utiliser pour éviter de gaspiller des ressources.

    Merci
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  2. #2
    Rédacteur
    Avatar de Zavonen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 772
    Détails du profil
    Informations personnelles :
    Âge : 76
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 772
    Points : 1 913
    Points
    1 913
    Par défaut
    Pour moi il ne sert à rien de construire des objets que l'on n'utilise pas.
    La construction de ces objets peut être longue, la place qu'ils occupent en mémoire peut être importante.
    Si on ne fait rien au niveau global des résultats du filtrage que l'appel d'une méthode pour les éléments filtrés il n'y a aucune raison d'utiliser une 'list comprehension'.
    La boucle classique simple me parait plus adaptée.
    Ce qu'on trouve est plus important que ce qu'on cherche.
    Maths de base pour les nuls (et les autres...)

  3. #3
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Je suis d'accord avec Zavonen, mais plus pour des raisons stylistiques que pour des raisons de performances ou de gaspillage des ressources.
    Maintenant, si la méthode start retourne quelque chose d'intéressant, et que tu fais quelque chose du résultat, ce serait une autre histoire...

    On peut éviter la création d'une liste inutile et rendre ça un peu plus joli comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def execute(it):
        for e in it: pass
     
    execute(t.start() for t in tab if t ...)
    * On utilise une generator expression au lieu d'une list comprehension
    * Mais alors, on doit itérer dessus pour que les fonctions soient appelées
    * On abstrait l'itération dans une fonction, et on en profite pour lui donner un nom qui insiste bien sur le fait qu'on l'appelle pour ses effets de bords (càd qu'on se fout de la valeur de retour).

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    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 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Salut,

    map(toto.start, tab), pour rappeler que les méthodes sont aussi des fonctions appliquées à une instance.
    Sinon, en passant par les generators:
    start = ( it.start() for it in tab if...)
    On peut se contenter de: list(start) pour forcer la chose i.e. pourquoi passer par execute.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    En ce qui me concerne, je privilégie systématiquement la clarté du code, de sorte que dans 6 mois, je verrai du 1er coup d'œil ce que ça doit faire. Je préfère donc nettement la solution basique:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    for t in tab:
        t.start()
    Comme en plus cette solution 'basique' est très facilement adaptable (filtrage) et ne pose pas de pb de consommation de ressources inutiles, c'est encore mieux.

    Tyrtamos
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  6. #6
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    On peut se contenter de: list(start) pour forcer la chose i.e. pourquoi passer par execute.
    - W
    Oui, bah, c'était pour éviter l'allocation de mémoire de la liste, et insister sur le fait que le résultat (la liste de résultats) ne nous intéresse pas.

    Mais bon, c'était surtout pour élaborer un peu sur la question. Concrètement, je ferais comme Zavonen et tyrtamos, une boucle "impérative".

  7. #7
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Ouais, merci beaucoup pour tous ces avis éclairés.

    J'avais bien l'intuition qu'une liste comprehension c'était pas optimum comme soluce.

    En lisant la réponse de wiztricks, je me suis souvenu que cette façon de coder que je m'étais appropriée avait en fait été suggérée par je ne sais plus quel tuto/faq Python qui disait que les list comprehension étaient plus rapides que les map.

    J'ai pas retrouvé le tuto/faq dans lequel j'ai lu ça mais j'en ai trouvé un autre qui dit sensiblement la même chose => http://www.biologeek.com/bonnes-prat...comprehensions Et cet autre aussi qui dit en plus que bientôt les map disparaitront => http://inaps.org/articles/developpem...liste-exemples
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  8. #8
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir,

    Désolé pour l'incursion dans le sujet mais:
    Citation Envoyé par dividee Voir le message
    c'était pour éviter l'allocation de mémoire de la liste, et insister sur le fait que le résultat (la liste de résultats) ne nous intéresse pas.
    Citation Envoyé par dividee Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def execute(it):
        for e in it: pass
     
    execute(t.start() for t in tab if t ...)
    Ou est l'occupation mémoire par rapport à un:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class toto:
        def start(self):
            print ('pass')
     
    tab=[toto(), toto(), toto()]
     
    for it in (items for items in tab if items.start):
        it.start()
    ?

    Merci
    Merci d'utiliser le forum pour les questions techniques.

  9. #9
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    @Sve@r:
    map, filter & co ne disparaîtront certainement jamais. Au pire, ils ne seront plus des builtins mais dans un module de la librairie standard. Et si ça arrive, ça m'étonnerais que ce soit de si tôt.

    Si tu lis jusqu'au bout le premier lien que tu as posté, tu verras que ce n'est pas aussi simple que ça, niveau performance. Les lists comprehensions sont plus rapides sur de petites quantités de données, car l'overhead des appels de fonctions (à map ou filter) est relativement élevé. Mais sur des listes plus grandes, map et filter sont en fait plus rapides. J'ai pu le vérifier en Python 2.7

    @PauseKawa: ton code évite aussi l'allocation de mémoire, c'était par rapport à une list comprehension ou un map

  10. #10
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Citation Envoyé par dividee Voir le message
    Si tu lis jusqu'au bout le premier lien que tu as posté, tu verras que ce n'est pas aussi simple que ça, niveau performance. Les lists comprehensions sont plus rapides sur de petites quantités de données, car l'overhead des appels de fonctions (à map ou filter) est relativement élevé. Mais sur des listes plus grandes, map et filter sont en fait plus rapides. J'ai pu le vérifier en Python 2.7
    Étonnant. Il me semblais que l'utilisation directe de built in (ainsi que de locales) était toujours plus rapide du fait du code. Un exemple ?
    Merci d'utiliser le forum pour les questions techniques.

  11. #11
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Citation Envoyé par PauseKawa Voir le message
    Étonnant. Il me semblais que l'utilisation directe de built in (ainsi que de locales) était toujours plus rapide du fait du code. Un exemple ?
    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
    from time import clock
     
    def t(f,*args):
        t0 = clock()
        f(*args)
        return clock() - t0
     
    from random import randint
    l = [randint(-100,100) for _ in xrange(1000000)]
     
    def map1():
        map(abs,l)
     
    def map2():
        [abs(i) for i in l]
     
    def odd(n):
        return n & 1
     
    def map_filter1():
        map(abs, filter(odd, l))
     
    def map_filter2():
        [abs(i) for i in l if odd(i)]
     
    def map_filter3():
        [abs(i) for i in l if i & 1]
     
    print 'map1:', t(map1), 's'
    print 'map2:', t(map2), 's'
    print 'map_filter1:', t(map_filter1), 's'
    print 'map_filter2:', t(map_filter2), 's'
    print 'map_filter3:', t(map_filter3), 's'
    Résultat:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    map1: 0.089907232765 s
    map2: 0.149724136161 s
    map_filter1: 0.225532160591 s
    map_filter2: 0.280151276031 s
    map_filter3: 0.146270705827 s
    Ce qui est réellement coûteux, ce sont les appels de fonctions. map_filter3 a l'avantage quand on 'inline' la fonction, ce qui n'est pas possible avec map & filter sans passer par une lambda, qui n'offre aucun gain de temps par rapport à un appel de fonction.

    Conclusion: un "simple" map est plus rapide qu'une list comprehension (pour peu qu'il y ait plus d'une poignée d'éléments), mais s'il faut également faire du filtrage, la list compr. sera normalement plus efficace si elle permet d'éviter des appels de fonctions.

  12. #12
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par dividee Voir le message
    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
    from time import clock
     
    def t(f,*args):
        t0 = clock()
        f(*args)
        return clock() - t0
     
    from random import randint
    l = [randint(-100,100) for _ in xrange(1000000)]
     
    def map1():
        map(abs,l)
     
    def map2():
        [abs(i) for i in l]
     
    def odd(n):
        return n & 1
     
    def map_filter1():
        map(abs, filter(odd, l))
     
    def map_filter2():
        [abs(i) for i in l if odd(i)]
     
    def map_filter3():
        [abs(i) for i in l if i & 1]
     
    print 'map1:', t(map1), 's'
    print 'map2:', t(map2), 's'
    print 'map_filter1:', t(map_filter1), 's'
    print 'map_filter2:', t(map_filter2), 's'
    print 'map_filter3:', t(map_filter3), 's'
    Résultat:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    map1: 0.089907232765 s
    map2: 0.149724136161 s
    map_filter1: 0.225532160591 s
    map_filter2: 0.280151276031 s
    map_filter3: 0.146270705827 s
    Ce qui est réellement coûteux, ce sont les appels de fonctions. map_filter3 a l'avantage quand on 'inline' la fonction, ce qui n'est pas possible avec map & filter sans passer par une lambda, qui n'offre aucun gain de temps par rapport à un appel de fonction.

    Conclusion: un "simple" map est plus rapide qu'une list comprehension (pour peu qu'il y ait plus d'une poignée d'éléments), mais s'il faut également faire du filtrage, la list compr. sera normalement plus efficace si elle permet d'éviter des appels de fonctions.
    Joli exemple de Benchmarck. Maîtrise du Python à un niveau qui me fait envie. Mais *arg n'est pas utile n'est-ce pas ???

    Sinon de mon coté aujourd'hui j'ai repris tous mes codes et remplacé chaque list comprehension inutiles par une classique boucle for. Heureusement j'avais la commande magique pour trouver les sources concernés => grep -l " [" $(find . -name "*.py" -print)
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  13. #13
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Joli exemple de Benchmarck. Maîtrise du Python à un niveau qui me fait envie. Mais *arg n'est pas utile n'est-ce pas ???
    Merci pour le compliment. Effectivement, *args n'est pas utile, mais j'ai écris la fonction [i]t[/t] en premier et je ne savais pas encore exactement comment j'allais l'utiliser...

  14. #14
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir,

    Citation Envoyé par dividee Voir le message
    Ce qui est réellement coûteux, ce sont les appels de fonctions. map_filter3 a l'avantage quand on 'inline' la fonction, ce qui n'est pas possible avec map & filter sans passer par une lambda, qui n'offre aucun gain de temps par rapport à un appel de fonction.

    Conclusion: un "simple" map est plus rapide qu'une list comprehension (pour peu qu'il y ait plus d'une poignée d'éléments), mais s'il faut également faire du filtrage, la list compr. sera normalement plus efficace si elle permet d'éviter des appels de fonctions.
    J'avais bien compris dividee.
    Citation Envoyé par PauseKawa Voir le message
    Étonnant. Il me semblais que l'utilisation directe de built in (ainsi que de locales) était toujours plus rapide du fait du code. Un exemple ?
    Merci pour l'exemple

    Bon code
    Merci d'utiliser le forum pour les questions techniques.

Discussions similaires

  1. Comment optimiser plusieurs boucles FOR-END imbriquées
    Par totoc1001 dans le forum MATLAB
    Réponses: 26
    Dernier message: 13/05/2007, 17h59
  2. [Tableaux] Optimisation de boucles
    Par xdoreau dans le forum Langage
    Réponses: 4
    Dernier message: 12/02/2007, 11h28
  3. Optimisation de boucle 'while..do'
    Par delphi5user dans le forum Delphi
    Réponses: 10
    Dernier message: 25/07/2006, 22h37
  4. Probleme optimisation de boucles
    Par panda31 dans le forum C
    Réponses: 13
    Dernier message: 06/04/2006, 15h10
  5. Réponses: 4
    Dernier message: 17/01/2006, 19h17

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