Autodidacte Projets personnels

Python — Erreurs faites pendant le projet password-checker

Pas une liste pour culpabiliser, mais une carte des patterns d’erreur à reconnaître pour ne plus les refaire. Chaque erreur ici est typique du débutant et conceptuellement instructive — donc précieuse pour qui apprend Python en ce moment.

Le contexte : pendant mon premier projet Python sérieux (un vérificateur de force de mot de passe), j’ai accumulé une série d’erreurs très révélatrices de l’écart entre la façon dont un débutant pense et la façon dont Python fonctionne réellement.


1. Le piège du return manquant

Ce que j’ai fait

def has_length(password):
    enough_length = False
    if len(password) >= 8:
        enough_length = True
    # ← pas de return !

Résultat : has_length("longpassword") renvoyait None au lieu de True.

Pourquoi c’est un piège

Une fonction Python sans return explicite renvoie automatiquement None. Pas d’erreur visible, mais ta fonction “fonctionne” et ne fait rien d’utile.

Bon réflexe

def has_length(password):
    return len(password) >= 8

Comment le détecter en avance

Teste tes fonctions immédiatement avec print(ma_fonction(...)). Si tu vois None, tu as oublié un return.


2. .upper() vs .isupper() — la méthode qui transforme vs celle qui teste

Ce que j’ai fait

if char.upper():     # ← FAUX
    has_upper = True

Pourquoi c’est un bug grave

Avec if char.upper():, tu testes en réalité if "A": qui est truthy (toute chaîne non vide est considérée vraie). La condition est donc toujours vraie, peu importe le caractère.

Le concept derrière : truthy / falsy

En Python, dans un if, n’importe quelle valeur est convertie en booléen :

ValeurÉvaluée comme
"", [], {}, (), set(), 0, None, False (toute collection vide)Falsy (faux)
Tout le reste (texte non vide, nombre ≠ 0, collection non vide, etc.)Truthy (vrai)

Bon réflexe


3. elif au lieu de if indépendants

Ce que j’ai fait

score = 0
if has_length(password):
    score += 1
elif has_number(password):       # ← FAUX (devrait être if, pas elif)
    score += 1
elif has_uppercase(password):
    score += 1
# ...

Pourquoi c’est un bug

elif = “sinon si”. Python ne teste les elif que si tous les if/elif précédents sont faux. Dès qu’un bloc passe, les autres sont sautés. Score maximum : 1.

Le bon pattern : des if indépendants

Pour cumuler des conditions, chaque test doit être un if séparé :

score = 0
if has_length(password):
    score += 1
if has_number(password):              # ← if "normal"
    score += 1
if has_uppercase(password):
    score += 1

Mémo

Tu veux…Tu utilises
Une alternative exclusive (un seul chemin pris)if / elif / else
Plusieurs conditions indépendantes qui s’additionnentifs séparés
Tester si au moins une condition est vraieif A or B or C: (renvoie UN seul résultat, ne cumule pas)

4. Confusion entre or et “cumul”

Ce que j’ai fait

if has_length(password) or has_number(password) or has_uppercase(password):
    score += 1

Pensé : “ça va ajouter 1 pour chaque condition vraie”. Réalité : or produit un seul résultat (“est-ce qu’au moins une est vraie ?”). Le score += 1 ne s’exécute donc qu’une seule fois maximum.

Bon réflexe

Précision pour plus tard : en Python, or renvoie en réalité l’un de ses deux opérandes, pas forcément un booléen. "" or "abc" vaut "abc", et "a" and "b" vaut "b".


5. Le copier-coller piégeux

Ce que j’ai fait

def has_lowercase(password):
    for char in password:
        if char.lower():
            has_upper = True       # ← oublié de renommer
            return has_upper       # ← oublié de renommer

J’avais copié has_uppercase et oublié de renommer les occurrences de has_upper en has_lower. Au passage, la copie a aussi recopié les erreurs des parties 1 et 2 : char.lower() au lieu de char.islower(), et aucun return False après la boucle.

Et plus tard :

checks = [has_length, has_number, has_uppercase, has_uppercase, has_specialcharacters]
#                                                ↑↑↑
#                                  copié 2 fois, has_lowercase oublié

Pourquoi c’est dangereux

Python n’a aucun moyen de détecter ce genre d’erreur. Le code tourne, mais ment : pas d’exception levée, pas d’avertissement, juste un résultat faux.

Bons réflexes


6. print dans une fonction qui devrait return

Ce que j’ai fait

def evaluate_password(password):
    # ... calcul du score ...
    if score >= 4:
        print(f"Strong, {score}/5")     # ← imprime
    # pas de return → renvoie None

print(evaluate_password(password))      # → imprime "Strong, X/5" puis "None"

Le principe : séparation des responsabilités

Par défaut, une fonction devrait faire UNE chose : calculer et renvoyer un résultat. C’est au code appelant de décider quoi faire de ce résultat (afficher, sauver, envoyer, ignorer).

def evaluate_password(password):
    # ... calcul ...
    if score >= 4:
        return f"Strong, {score}/5"      # ← renvoie, n'imprime pas

print(evaluate_password(password))       # le caller décide d'imprimer

Pourquoi c’est mieux

L’exception

Une fonction conçue spécifiquement pour afficher (ex. print_report(), display_results()) — son nom dit clairement son intention. Là, le print à l’intérieur est légitime et la fonction ne renvoie souvent rien volontairement.


7. La f-string n’est PAS liée à print

Ce que je pensais

“Une f-string ne peut s’utiliser que dans un print.”

Réalité

Une f-string est juste une chaîne de caractères avec interpolation. C’est une valeur, pas une action. Tu peux faire tout ce que tu fais avec une chaîne normale :

name = "Alice"
msg = f"Hello, {name}"          # construction
print(msg)                       # afficher
return msg                       # renvoyer (dans une fonction)
len(msg)                         # mesurer (12)
msg.upper()                      # transformer
fichier.write(msg)               # écrire dans un fichier

Le concept derrière : valeur vs action

Une valeur (string, nombre, liste, bool…) existe en soi. Ce que tu fais avec est une action séparée (print, return, =, write, etc.). Construire une valeur et l’utiliser sont deux étapes distinctes — c’est une distinction qu’on retrouve dans tous les langages.


8. while quand il fallait for

Ce que j’avais essayé

while has_length(password) or has_number(password) or ... :
    score += 1
    break

J’espérais cumuler des points.

Pourquoi while ne convenait pas

Le bon outil : for sur une collection

checks = [has_length, has_number, has_uppercase, has_lowercase, has_specialcharacters]
for check in checks:
    if check(password):
        score += 1

Mémo : quel outil pour quel besoin ?

BesoinOutil
Répéter une action tant que une condition est vraiewhile
Faire quelque chose pour chaque élément d’une collectionfor
Faire un test booléen à un instant donnéif

9. Les fonctions sont des objets (first-class functions)

Ce que j’ai découvert

checks = [has_length, has_number, has_uppercase, has_lowercase, has_specialcharacters]
#                                  ↑ sans parenthèses !

Une fonction en Python est une valeur au même titre qu’un nombre ou une chaîne. Tu peux la stocker dans une variable, la mettre dans une liste, la passer en argument.

La différence cruciale

SyntaxeSignification
has_lengthLa fonction elle-même (objet)
has_length(password)Appelle la fonction et donne son résultat

C’est la différence entre une recette (instructions, posée dans le livre) et cuisiner le plat (exécution de la recette).

Pourquoi c’est puissant

Tu peux écrire :

for check in checks:
    if check(password):    # ← on appelle ici, avec ()
        score += 1

Au lieu de 5 if répétés. C’est le pattern DRY (Don’t Repeat Yourself) en action — un des principes fondamentaux de la programmation propre.


10. Bonus : ce que j’ai bien fait (à reproduire)

Pas que des erreurs ! Ces réflexes étaient bons :


Conseil de relecture : revisite cette fiche dans 3 jours, puis 1 semaine, puis 1 mois. Si à chaque relecture tu te dis “ah oui c’est évident”, c’est que c’est rentré. Sinon, ré-applique le pattern dans un nouveau projet.