Amélioration d'un code de débutant
Résolu/Fermé
spoiledog
-
Modifié le 19 mai 2021 à 16:34
mamiemando Messages postés 33393 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 28 novembre 2024 - 30 mai 2021 à 22:33
mamiemando Messages postés 33393 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 28 novembre 2024 - 30 mai 2021 à 22:33
A voir également:
- Amélioration d'un code de débutant
- Code ascii de a - Guide
- Code puk bloqué - Guide
- Code de déverrouillage oublié - Guide
- Code activation windows 10 - Guide
- Comment créer un qr code - Guide
7 réponses
mamiemando
Messages postés
33393
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
28 novembre 2024
7 803
Modifié le 19 mai 2021 à 17:03
Modifié le 19 mai 2021 à 17:03
Bonjour,
Le programme est déjà bien écrit comme tel, même si on peut effectivement y apporter quelques améliorations :
Exemple :
... devient :
Tu peux aussi utiliser la fonction
Bonne chance
Le programme est déjà bien écrit comme tel, même si on peut effectivement y apporter quelques améliorations :
- Tu devrais éviter de mélanger des mots anglais et français (e.g. find_oeuvre)
- Plutôt que d'utiliser
"????"
tu peux utiliserNone
. - Certains fonctions s'écrivent de manière plus concise e.g. avec des comprehension-list.
- Généralement on découple l'affichage (donc dans ton cas, des
print
) et les fonctions qui explore les données (donc tesprint
devraient être a l'extérieur de ces fonctions) - La séquence d'échappement
print("\033[94m Outil ...")
est spécifique aux terminaux Linux, donc pas forcément recommandée. - Le programme devrait quitter avec par exemple
sys.exit(1)
quand l'exception ValueError est levée. Cela correspond au code d'erreur. Si le code d'erreur vaut 0 (comportement par défaut), c'est que le programme s'est bien déroulé. Sinon le code d'erreur est documenté et selon sa valeur, peut permettre au programme appelant d'adapter son comportement. Cela permet de détecter au niveau de l'appel de ton programme python que celui-ci a planté.
Exemple :
def find_oeuvres(sub): # Trouve les oeuvres qui ont pour titre un bout de la recherche resultat = [] # On stockera ici les résultats for oeuvre in oeuvres: # On boucle sur les oeuvres if sub in oeuvre['titre']: resultat.append(oeuvre) # Quand on trouve une oeuvre qui match le titre on l'ajoute à resultat print(resultat)
... devient :
def find_oeuvres(sub): # Trouve les oeuvres qui ont pour titre un bout de la recherche return [ oeuvre for oeuvre in oeuvres # On boucle sur les oeuvres if sub in oeuvre['titre'] ]
Tu peux aussi utiliser la fonction
filter.
def find_oeuvres(sub): # Trouve les oeuvres qui ont pour titre un bout de la recherche return filter( lambda oeuvre: sub in oeuvre['titre'], oeuvres )
Bonne chance
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
Ambassadeur
1 554
19 mai 2021 à 16:42
19 mai 2021 à 16:42
bonjour
la fonction 4 me semble particulièrement mal conçue.
réfléchis à la manière dont tu chercherais la réponse dans la vie réelle.
par exemple si tu avais une bibliothèque avec 50 livres, et tu dois trouver l'auteur du livre ayant le plus de pages.
la fonction 4 me semble particulièrement mal conçue.
réfléchis à la manière dont tu chercherais la réponse dans la vie réelle.
par exemple si tu avais une bibliothèque avec 50 livres, et tu dois trouver l'auteur du livre ayant le plus de pages.
Phil_1857
Messages postés
1872
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
28 février 2024
168
Modifié le 20 mai 2021 à 11:18
Modifié le 20 mai 2021 à 11:18
Bonjour,
Ah je vois que tu n'as pas adopté la structure que je te proposais dans ton appel précédent (problème de boucle for) ...
et la façon plus précise d'afficher une erreur dans except
D'ailleurs, tu devrais fermer l'autre appel
Ah je vois que tu n'as pas adopté la structure que je te proposais dans ton appel précédent (problème de boucle for) ...
et la façon plus précise d'afficher une erreur dans except
D'ailleurs, tu devrais fermer l'autre appel
oui je l'ai changé carrément et pour le try je n'ai pas compris comment le faire alors je l'ai laissé
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
Phil_1857
Messages postés
1872
Date d'inscription
lundi 23 mars 2020
Statut
Membre
Dernière intervention
28 février 2024
168
Modifié le 20 mai 2021 à 12:51
Modifié le 20 mai 2021 à 12:51
Ben, c'est tout simple, c'est comme je te l'ai montré:
en début de code, tu écris:
et au lieu de
tu écris
N'oublie pas de marquer l'appel précédent comme résolu
en début de code, tu écris:
import sys
et au lieu de
print("Vérifiez vos saisies!!")
tu écris
print(sys.exc_info()[1])
N'oublie pas de marquer l'appel précédent comme résolu
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
1 554
>
spoiledog
20 mai 2021 à 22:11
20 mai 2021 à 22:11
pas toujours simple si tu n'es pas inscrit sur le forum.
je l'ai fait.
je l'ai fait.
spoiledog
>
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
20 mai 2021 à 22:57
20 mai 2021 à 22:57
tu as marqué cet appel comme résolu, on parlait du précédant (Problème de boucle for) :(
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
1 554
>
spoiledog
20 mai 2021 à 23:12
20 mai 2021 à 23:12
corrigé.
mamiemando
Messages postés
33393
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
28 novembre 2024
7 803
Modifié le 21 mai 2021 à 15:27
Modifié le 21 mai 2021 à 15:27
Juste pour revenir à la remarque sur de la fonction liée à la question 4.
1)a) Au début tu cherches la durée max. Il n'y a pas besoin de mémoriser toutes les durées pour maintenir au cours du parcours la durée max trouvé jusque là. Ainsi te pourrais te passer de la liste durees et économiser de la mémoire. On parle de footprint (empreinte) mémoire, et si n désigne le nombre d'oeuvre, il est ici en O(n) (il croit linéairement avec le nombre d'oeuvre).
Algorithmiquement cela revient à modifier ta boucle comme suit :
Tu as alors un footprint en O(1).
Mais on peut faire plus élégant grâce :
Ainsi le bloc précédent s'écrit tout simplement :
1)b) L'autre aspect pour concevoir un algorithme est sa complexité. Ici tu as deux boucles
2) La seconde partie est un filtrage (qui mélange aussi la partie affichage, qui devrait être réalisé à l'extérieur de la fonction pour dissocier la manipulation des données et leur présentation). Je ne reviens pas trop sur comment faire le filtrage de manière plus élégante, j'en ai déjà parlé dans mon message précédent. Pour voir l'intérêt de cette remarque, il faut t'imaginer que ton programme pourrait un jour être utilisé dans une interface graphique. Dans ce cas là, la fonction de recherche reste la même, mais le print devrait être remplacé par ce qu'il faut pour corriger ton interface graphique. C'est le point de départ de ce qu'on appelle en architecture logicielle le modèle MVC.
Ainsi ta fonction
Bonne chance
1)a) Au début tu cherches la durée max. Il n'y a pas besoin de mémoriser toutes les durées pour maintenir au cours du parcours la durée max trouvé jusque là. Ainsi te pourrais te passer de la liste durees et économiser de la mémoire. On parle de footprint (empreinte) mémoire, et si n désigne le nombre d'oeuvre, il est ici en O(n) (il croit linéairement avec le nombre d'oeuvre).
Algorithmiquement cela revient à modifier ta boucle comme suit :
duree_max = None for oeuvre in oeuvres: duree = oeuvre["duree"] if duree_max is None or duree > duree_max: duree_max = duree
Tu as alors un footprint en O(1).
Mais on peut faire plus élégant grâce :
- à la fonction
max()
en python. ; - au concept de générateurs (c'est typiquement ce que retourne la fonction
filter
)
Ainsi le bloc précédent s'écrit tout simplement :
duree_max = max(oeuvre["duree"] for oeuvre in oeuvres)
1)b) L'autre aspect pour concevoir un algorithme est sa complexité. Ici tu as deux boucles
forsur les oeuvres (chacune en O(n)), donc l'algorithme fonctionne en O(n). Passer à la fonction
maxne change rien de ce côté, car sa complexité est aussi O(n).
2) La seconde partie est un filtrage (qui mélange aussi la partie affichage, qui devrait être réalisé à l'extérieur de la fonction pour dissocier la manipulation des données et leur présentation). Je ne reviens pas trop sur comment faire le filtrage de manière plus élégante, j'en ai déjà parlé dans mon message précédent. Pour voir l'intérêt de cette remarque, il faut t'imaginer que ton programme pourrait un jour être utilisé dans une interface graphique. Dans ce cas là, la fonction de recherche reste la même, mais le print devrait être remplacé par ce qu'il faut pour corriger ton interface graphique. C'est le point de départ de ce qu'on appelle en architecture logicielle le modèle MVC.
Ainsi ta fonction
longest_pieces_author()(pieces, comme il y en a plusieurs) deviendrait plutôt, si on renomme tes variables globales en majuscules, conformément aux recommandations PEP-8 :
def longest_pieces_author(oeuvres): duree_max = max(oeuvre['duree'] for oeuvre in oeuvres) return [ oeuvre for oeuvre in oeuvres if oeuvre['duree'] == duree_max ] from pprint import pprint pprint(longest_pieces_author(OEUVRES))
Bonne chance
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
1 554
Modifié le 21 mai 2021 à 16:22
Modifié le 21 mai 2021 à 16:22
Tout cela ne change rien au défaut principal de l'algorithme proposé: la présence de la seconde boucle, révoltante d'inefficacité.
Pourquoi travailler en deux passes alors qu'une suffit?
Pourquoi travailler en deux passes alors qu'une suffit?
def longest_pieces_author(oeuvres): duree_max = None for oeuvre in oeuvres: duree = oeuvre["duree"] if duree_max is None or duree > duree_max: duree_max = duree auteur = oeuvre['comp'] return auteur
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
1 554
>
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
21 mai 2021 à 16:30
21 mai 2021 à 16:30
ou bien, si on veut récupérer plusieurs auteurs:
def longest_pieces_author(oeuvres): duree_max = None for oeuvre in oeuvres: duree = oeuvre["duree"] if duree_max is None or duree > duree_max: duree_max = duree auteurs= [] auteurs.append(oeuvre['comp']) elif duree == duree_max auteurs.append(oeuvre['comp']) return auteurs
mamiemando
Messages postés
33393
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
28 novembre 2024
7 803
>
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
Modifié le 28 mai 2021 à 22:15
Modifié le 28 mai 2021 à 22:15
Hé bien... ma version ne doit pas être si sale que ça :-) Je te laisse tester sur ton PC.
Ensuite si tu testes dans un jupyter notebook :
Ça peut surprendre, d'autant que ma version est significativement plus rapide (facteur 5). L'explication est double :
Mais je comprends que tu aies tiqué, tu as sans doute fait tes armes sur du C ou un langage dans le genre, où ta remarque aurait sans doute été justifiée (pas de fonction max + léger gain de temps à faire une boucle for au lieu de deux -- en tout cas pas un facteur 2).
from random import randint OEUVRES = [ { "title" : "title%s" % i, "duration" : randint(0, 10) } for i in range(100000) ] def longest_pieces_author_mando(oeuvres): duree_max = max(oeuvre['duration'] for oeuvre in oeuvres) return [ oeuvre for oeuvre in oeuvres if oeuvre['duration'] == duree_max ] def longest_pieces_author_ygbe(oeuvres): duree_max = None for oeuvre in oeuvres: duree = oeuvre["duration"] if duree_max is None or duree > duree_max: duree_max = duree auteurs = [oeuvre] elif duree == duree_max: auteurs.append(oeuvre) return auteurs
Ensuite si tu testes dans un jupyter notebook :
%time
x = longest_pieces_author_mando(OEUVRES)
CPU times: user 2 µs, sys: 0 ns, total: 2 µs
Wall time: 3.34 µs
%time
x = longest_pieces_author_ygbe(OEUVRES)
CPU times: user 8 µs, sys: 0 ns, total: 8 µs
Wall time: 15.7 µs
Ça peut surprendre, d'autant que ma version est significativement plus rapide (facteur 5). L'explication est double :
- la complexité en temps de
longest_pieces_author_mando
etlongest_pieces_author_ygbe
est dans les deux cas O(n), donc ta proposition n'est pas plus rapide sur le plan théorique ; - en terme d'implémentation
max
va très vite ; le fait d'utiliser des générateurs et comprehension-list plutôt que des boucles for comme tu l'as fait permet à python d'exécuter le code plus rapidement.
Mais je comprends que tu aies tiqué, tu as sans doute fait tes armes sur du C ou un langage dans le genre, où ta remarque aurait sans doute été justifiée (pas de fonction max + léger gain de temps à faire une boucle for au lieu de deux -- en tout cas pas un facteur 2).
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
1 554
>
mamiemando
Messages postés
33393
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
28 novembre 2024
21 mai 2021 à 17:32
21 mai 2021 à 17:32
C'est uniquement une question de principe.
Python est souvent utilisé pour apprendre les concepts de programmation.
Dans ce contexte, je pense préférable d'éviter tout ce qui est trop spécifique à Python, cela distrait des concepts à apprendre. Et compliquera le passage à un autre langage.
Surtout pour des débutants, il me semble important de concevoir un algorithme efficace, puis de le transcrire dans un langage.
Celui qui ne se rend pas compte qu'une passe suffit utilisera toujours deux passes, même si les données ne sont pas en memoire, ou trop nombreuses que pour être mémorisées simultanément.
Python est souvent utilisé pour apprendre les concepts de programmation.
Dans ce contexte, je pense préférable d'éviter tout ce qui est trop spécifique à Python, cela distrait des concepts à apprendre. Et compliquera le passage à un autre langage.
Surtout pour des débutants, il me semble important de concevoir un algorithme efficace, puis de le transcrire dans un langage.
Celui qui ne se rend pas compte qu'une passe suffit utilisera toujours deux passes, même si les données ne sont pas en memoire, ou trop nombreuses que pour être mémorisées simultanément.
mamiemando
Messages postés
33393
Date d'inscription
jeudi 12 mai 2005
Statut
Modérateur
Dernière intervention
28 novembre 2024
7 803
>
yg_be
Messages postés
23356
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
28 novembre 2024
Modifié le 30 mai 2021 à 22:37
Modifié le 30 mai 2021 à 22:37
Je suis d'accord sur le fait que savoir factoriser du code est important et qu'il faut savoir le voir.
Ensuite par rapport au débat de départ :
Ensuite le problème c'est aussi la justesse du code. Dans la fonction que tu proposes je viens de m'apercevoir d'un problème : la fonction ne marchera que si la liste d'œuvre est non vide. C'est dû à une erreur de design dans ta fonction : la liste d'auteurs devrait être (quel que soit le langage) être déclarée en dehors de la boucle
Ensuite par rapport au débat de départ :
- Soit on parle de complexité théorique (en mémoire ou temps) et faire une boucle ou deux revient au même (par exemple entre un tri rapide et un tri à bulle, il n'y a pas d'hésitation). Par rapport à ta dernière remarque, qui en gros parle de l'empreinte mémoire, tu peux constater que les deux solutions sont équivalentes.
- Soit on parle de performance observée et dans ce cas, la solution que je propose est plus rapide, plus "modulaire", et plus lisible. Dans ce cas, il faut garder en tête qu'en python, une boucle classique coûte plus cher qu'une comprehension-list ou qu'une built-in.
Ensuite le problème c'est aussi la justesse du code. Dans la fonction que tu proposes je viens de m'apercevoir d'un problème : la fonction ne marchera que si la liste d'œuvre est non vide. C'est dû à une erreur de design dans ta fonction : la liste d'auteurs devrait être (quel que soit le langage) être déclarée en dehors de la boucle
for.