Algorithmique Appliquée

BTS SIO SISR

Les bases du langage Python

Plan

  • Conditions
  • Chaînes de caractères et encodage de caractères
  • Entrée et sortie standard
  • Boucles "Tant Que"
  • Boucles "Pour"
  • Discussion sur les différences entre Scratch et Python
  • Style, commentaires et PEP 8

Conditions

Branche

  • Un algorithme doit souvent prendre des décisions.
  • En fonction de la valeur d'une expression Booléenne, l'interpréteur va suivre une branche ou une autre.

Exemple de branchement

  • On peut visualiser graphiquement les branches.
  • Pseudo-code équivalent :
Si la valeur de l'expression Test renvoie Vrai:
    Exécute le Bloc de code 1
Sinon:
    Exécute le Bloc de code 2
  • En anglais :
    • si ➡️ if
    • sinon ➡️ else

Conditions en Python

  • La forme de base est la suivante :
if Test:
    Bloc de code 1
else:
    Bloc de code 2
  • Attention aux : et à l'indentation de 4 espaces.
  • Attention à la casse : les mots clés if et else sont en minuscule.

Le else est facultatif

  • Une condition peut prendre tout simplement la forme :
if Test:
    Bloc de code # Exécuté si Test == True

Quelques exemples

texte = ""
taille = 175

if taille > 180:
    texte = "grand"
else:
    texte = "petit"

print(texte)

➡️ petit

Quelques exemples

texte = "petit"
taille = 175

if taille > 180:
    texte = "grand"

print(texte)

➡️ petit

if imbriqués

texte = ""
taille = 175

if taille > 180:
    if taille > 200:
        texte = "très grand"
    else:
        texte = "grand"
else:
    if taille > 155:
        texte = "moyen"
    else:
        texte = "petit"

print(texte)

➡️ moyen

Plus lisible avec les expressions Booléennes composées

texte = ""
taille = 175

if taille > 200:
    texte = "très grand"
elif taille > 180:
    texte = "grand"
elif taille > 155:
    texte = "moyen"
else:
    texte = "petit"

print(texte)

➡️ moyen

Expression conditionnelle dense

taille = 175
texte = "grand" if taille > 180 else "petit"
print(texte)

➡️ petit

Chaînes de caractères et encodage de caractères

Type str

  • En anglais, string signifie chaîne de caractères.
  • C'est une liste de caractères.
  • Cette liste commence et termine par " (ou ').
  • Cela n'a rien à voir avec certains vêtements...
  • str est la contraction de string.
  • Exemple :
taille = "petit"

Concaténation

entree = "avocat"
plat = "riz"
dessert = "chocolat"
espace = " "
repas = entree + espace + plat + espace + dessert

print(repas)

➡️ avocat riz chocolat

Multiplication scalaire

a = "a"
trois_a = 3 * a

print(trois_a)

➡️ aaa

Multiplication ?

a = "a"
a_voir = a * a

⬇️

TypeError: can't multiply sequence by non-int of type 'str'

Longueur

len est un diminutif de length, qui signifie longueur.

chaine = "abcde"
len(chaine)

➡️ 5

chaine = "papillon"
len(chaine)

➡️ 8

Indexation

chaine = "abcde"
chaine[0]

➡️ "a"

chaine = "abcde"
chaine[2]

➡️ "c"

Indexation négative

chaine = "abcde"
chaine[-1]

➡️ "e"

chaine = "abcde"
chaine[-3]

➡️ "c"

Indexation en dehors des limites

chaine = "abcde"
chaine[5]

⬇️

IndexError: string index out of range

Tranche entre 2 bornes

debut = 1
fin = 4
chaine = "abcde"
tranche = chaine[debut:fin]
print(tranche)

➡️ "bcd"

Note : en anglais, on parle de slicing.
Note 2 : la borne de fin est exclue.

Tranche à partir d'un index

debut = 2
chaine = "abcdefg"
tranche = chaine[debut:]
print(tranche)

➡️ "cdefg"

Tranche jusqu'à un index

fin = 5
chaine = "abcdefg"
tranche = chaine[:fin]
print(tranche)

➡️ "abcde"

Tranche non contiguë

debut = 1
fin = 10
saut = 2
chaine = "abcdefghijklmnop"
tranche = chaine[debut:fin:saut]
print(tranche)

➡️ "bdfhj"

Caractères spéciaux

  • On utilise le caractère d'échappement \ en préfixe des caractères spéciaux dans les chaînes de caractères.
  • Quelques exemples :
    • Guillements : \"
    • Tabulation : \t
    • Retour à la ligne : \n
    • Retour chariot : \r
    • Backslash : \\

Exemples de caractères spéciaux

print("lapin\rLu")

➡️ "Lupin"

print("C:\\Users\\mikado\\Documents")

➡️ "C:\Users\mikado\Documents"

print("\tValeur : \"Zorro\"")

➡️ " Valeur : "Zorro""

Conversion vers des nombres

chaine = "1234"
entier = int(chaine)
entier += 8765
print(entier)

➡️ 9999

chaine = "3.1415"
reel = float(chaine)
reel *= 2
print(reel)

➡️ 6.283

Conversion depuis des nombres

entier = 123
chaine = str(entier)
chaine *= 2
print(chaine)

➡️ "123123"

Encodage de caractères : ASCII

  • ASCII : American Standard Code for Information Interchange.
  • Encodage simple et compact : sur 7 bits (moins de 1 octet), on peut avoir jusqu'à caractères.
  • Chaque caractère est représenté par un nombre entre 0 et 127.
  • La table ASCII offre une correspondance entre les nombres et leurs caractères associés.

Limites du ASCII

  • こんにちは : Bonjour (Japonais)
  • 你好吗? Comment allez-vous ? (Mandarin)
  • شكرا لك : Merci (Arabe)
  • 🆘 😩 💻 : Je ne comprends rien au cours ! (Emoji)

Unicode

  • Unicode : Universal Coded Character Set.
  • L'objectif est de normaliser l'encodage de caractères, d'inclure un maximum de langues et autres besoins et d'optimiser la représentation numérique.
  • L'UTF-8 (pour Unicode Transformation Format - 8 bit) est le plus répandu.
  • UTF-8 utilise entre 1 et 8 octets pour représenter jusqu'à 1 112 064 caractères.

Encodage en Python

  • L'encodage par défaut d'un script Python est UTF-8.
  • On peut changer cela en ajoutant au tout début d'un fichier Python :
# -*- coding: ascii -*- 
  • Toutes les str d'un script Python utilisent cet encodage par défaut.

Changer l'encodage à la volée

texte_en_utf8 = "Bonjour !"
texte_en_ascii = texte_en_utf8.encode("ascii")
print(texte_en_ascii)

➡️ b'Bonjour !'

Le préfixe b signifie qu'il s'agit d'une chaîne binaire.

Entrée et sortie standard

Introduction aux I/O

  • En français, on parle d'Entrées/Sorties, soit E/S.
  • La langue anglaise prédomine en informatique.
  • La traduction en anglais d'E/S est I/O pour Input/Output.
  • On enlève en général le slash, ce qui donne IO.

Les fichiers standards

  • Les Systèmes d'Exploitation classiques comportent 3 fichiers standards :
    • stdin (index 0) : entrée texte standard.
    • stdout (index 1) : sortie texte standard.
    • stderr (index 2) : sortie d'erreur standard.

Lire dans stdin

  • stdin est une redirection vers le périphérique clavier.
  • Une lecture dans stdin signifie donc que l'on va lire ce que l'utilisateur écrit.
  • En Python, on utilise la fonction input pour lire dans stdin :
nom = input("Votre nom : ")

Ecrire dans stdout

  • stdout est une redirection vers la console.
  • Une écriture dans stdout va afficher le contenu dans la console.
  • En Python, on utilise la fonction print pour écrire dans stdout :
nom = input("Votre nom : ")
print(nom)

Ecrire dans stderr

  • stderr, tout comme stdout, est une redirection vers la console.
  • Une écriture dans stderr va afficher le contenu dans la console.
  • En Python, lorsqu'une exception est levée, un message d'erreur est affiché dans stderr par défaut.
  • On peut également utiliser sys.stderr.write :
import sys
sys.stderr.write("Oh mince ! dit Shipper")

Formattage simple

age_en_texte = input("Votre age : ")
age = int(age_en_texte)
print("Vous avez", age, "ans")

➡️ "Vous avez 42 ans"
(si age == 42)

Limites du formattage simple

ht = 69.5
tva = 1/5
taxe = round(ht * tva * 100) / 100
ttc = ht + taxe
label_ht = "Prix (HT) : "
label_ttc = "Prix (TTC) : "
print(label_ht, ht, "€\n", label_ttc, ttc, "€")

⬇️

Prix (HT) :  69.5 €
 Prix (TTC) :  83.4 €

Chaîne de caractères littérale formattée

ht = 69.5
tva = 1/5
taxe = round(ht * tva * 100) / 100
label_ht = "Prix (HT) :"
label_ttc = "Prix (TTC) :"
print(f"{label_ht:>12} {ht:.2f}€\n{label_ttc:>12} {ht + taxe:.2f}€")

⬇️

 Prix (HT) : 69.50€
Prix (TTC) : 83.40€

TP 03 - Initiation aux Environnements de Développement Intégrés avec pour but de manipuler des chaînes de caractères

  • Visual Studio Code est un Environnement de Développement Intégré.
  • Edité par Microsoft en JavaScript/Electron.
  • Gratuit et Open Source.
  • Linux, macOS, Windows.
  • Nombreuses extensions.
  • Lien vers le site officiel

TP : Usage d'un IDE et manipulation de chaînes

Lien vers le sujet de TP.

Boucles "Tant que"

Pourquoi des boucles ? ➿

  • Imaginons un programme qui prend en entrée un nombre et doit calculer la somme de 1 à ce nombre :
nombre = int(input("Entrez un nombre positif : "))
resultat = 0
if nombre == 1:
    resultat = 1
elif nombre == 2:
    resultat = 1 + 2
elif nombre == 3:
    resultat = 1 + 2 + 3
elif nombre == 4:
    resultat = 1 + 2 + 3 + 4
# etc.
print(resultat)

Pourquoi des boucles ?

On souhaite en fait ici exprimer :

Le commentaire # etc. dans la diapositive précédente ou l'ellipse ... dans la formule ci-dessus expriment tous les 2 une répétition.

Définition

  • Une boucle permet de répéter un ensemble d'instructions.
  • Une répétition de cet ensemble d'instructions s'appelle une itération.
  • L'ensemble d'instructions à répéter s'appelle le corps de la boucle.
  • Une boucle s'arrête lorsque sa condition de fin devient vraie.

Boucle "Tant Que"

Une boucle "Tant Que" peut s'exprimer ainsi en pseudo-code :

Tant Que la valeur de l'expression Test est Vraie:
    Exécute le Corps de la Boucle

En anglais : tant que ➡️ while

Exemple en Python

nombre = int(input("Entrez un nombre : "))

resultat = 0
i = 0
while i <= nombre:
    resultat += i
    i += 1

print(resultat)

➡️ 45 si nombre == 9

Evolution des valeurs

Itération i resultat nombre
0 0 0 9
1 1 1 9
2 2 3 9
3 3 6 9
4 4 10 9
5 5 15 9
6 6 21 9
7 7 28 9
8 8 36 9
9 9 45 9

Boucle infinie 💀

nombre = 9
resultat = 0
i = 0
while i <= nombre:
    resultat += i
    i -= 1

Continue

from math import sin

pi = 3.14159265
epsilon = 1e-6
actuel = -2 * pi
fin = 2 * pi
increment = pi / 6
while actuel < fin:
    sin_actuel = sin(actuel)
    precedent = actuel
    actuel += increment

    if abs(sin_actuel) < epsilon: # si sin(actuel) est proche de 0
        continue                  # évite les instructions suivantes
    
    valeur = 1 / sin_actuel
    print(f"x = {precedent:>9.6f} ;"
        f" sin(x) = {sin_actuel:>9.6f} ;"
        f" 1 / sin(x) = {valeur:>9.6f}")

Continue

x = -5.759587 ; sin(x) =  0.500000 ; 1 / sin(x) =  2.000000
x = -5.235988 ; sin(x) =  0.866025 ; 1 / sin(x) =  1.154701
x = -4.712389 ; sin(x) =  1.000000 ; 1 / sin(x) =  1.000000
x = -4.188790 ; sin(x) =  0.866025 ; 1 / sin(x) =  1.154701
x = -3.665191 ; sin(x) =  0.500000 ; 1 / sin(x) =  2.000000
x = -2.617994 ; sin(x) = -0.500000 ; 1 / sin(x) = -2.000000
x = -2.094395 ; sin(x) = -0.866025 ; 1 / sin(x) = -1.154701
x = -1.570796 ; sin(x) = -1.000000 ; 1 / sin(x) = -1.000000
x = -1.047198 ; sin(x) = -0.866025 ; 1 / sin(x) = -1.154701
x = -0.523599 ; sin(x) = -0.500000 ; 1 / sin(x) = -2.000000
x =  0.523599 ; sin(x) =  0.500000 ; 1 / sin(x) =  2.000000
x =  1.047198 ; sin(x) =  0.866025 ; 1 / sin(x) =  1.154701
x =  1.570796 ; sin(x) =  1.000000 ; 1 / sin(x) =  1.000000
x =  2.094395 ; sin(x) =  0.866025 ; 1 / sin(x) =  1.154701
x =  2.617994 ; sin(x) =  0.500000 ; 1 / sin(x) =  2.000000
x =  3.665191 ; sin(x) = -0.500000 ; 1 / sin(x) = -2.000000
x =  4.188790 ; sin(x) = -0.866025 ; 1 / sin(x) = -1.154701
x =  4.712389 ; sin(x) = -1.000000 ; 1 / sin(x) = -1.000000
x =  5.235988 ; sin(x) = -0.866025 ; 1 / sin(x) = -1.154701
x =  5.759587 ; sin(x) = -0.500000 ; 1 / sin(x) = -2.000000

Break

x = 1000 * 1000 * 1000
while True:
    if (x % 11 == 0) and (x % 27 == 0):
        break
    x -= 1
print(f"{x} est dans la table des 11 et des 27")

➡️ "999999891 est dans la table des 11 et des 27"

Boucles imbriquées

i = 1
while i < 4:
    line = ""
    j = 1
    while j < 4:
        line += f"{i * j:>3}"
        j += 1
    print(line)
    i += 1

⬇️

  1  2  3
  2  4  6
  3  6  9

Boucles "Pour" et "Bornes"

Intérêt

Admettons que nous ayons une liste de noms que nous souhaitons afficher dans la console :

liste = [ "Alan", "Ada", "Donald" ]
taille = len(liste)
i = 0
while i < taille:
    nom = liste[i]
    print(nom)
    i += 1

⬇️

Alan
Ada
Donald

Simplification

for nom in [ "Alan", "Ada", "Donald" ]:
    print(nom)

⬇️

Alan
Ada
Donald

Boucle "Pour"

Une boucle "Pour" peut s'exprimer ainsi en pseudo-code :

Pour chaque élément du containeur:
    Exécute le Corps de la Boucle sur cet élément

En anglais : pour ➡️ for

Autre exemple

for i in { 1, 2, 3 }:
    print(i)

⬇️

1
2
3

Retour sur les sommes

somme = 0
for i in { 1, 2, 3, 4, 5, 6, 7, 8, 9 }:
    somme += i
print(somme)

➡️ 45

Mais si on ne connait pas la limite supérieure ?

taille = int(input("Taille : "))
somme = 0
# for i in { 1, 2, 3, 4, 5, ..., taille }: # ce code est commenté
#     somme += i                           # comment fait-on ?
print(somme)

Solution : bornes

La fonction range permet de résoudre ce problème.
En anglais : borne ➡️ range.

taille = int(input("Taille : "))
somme = 0
for i in range(taille + 1):
    somme += i
print(somme)

Autre exemple

for i in range(3):
    print(i)

⬇️

0
1
2

Autrement dit :

Bornes de début et de fin

debut = 1
fin = 3
for i in range(debut, fin):
    print(i)

⬇️

1
2

Autrement dit :

Pas

debut = 1
fin = 6
pas = 2
for i in range(debut, fin, pas):
    print(i)

⬇️

1
3
5

TP 04 - Quelques algorithmes simples pour prendre en main les fondamentaux de l'algorithmique

  • Jupyter Notebook est supporté par Binder dans votre navigateur web et par Visual Studio Code.
  • Vous avez maintenant pu essayer les deux dans le cadre des précédents TPs.
  • Vous êtes libres d'utiliser l'environnement de votre choix à partir de ce TP.

TP : Algorithmes pour l'arithmétique simple

Lien vers le sujet de TP.

Différences entre Python et Scratch

Différences concernant les conditions

Différences concernant les boucles

Comment feriez-vous pour ré-implémenter le TP 01 Anjou Vélo Vintage en Python ?

Style, commentaires et PEP 8

Que pensez-vous du code suivant ?

from math import sin
var1=3.14159265;var2=1e-6;var3=-2*var1;var4=2*var1;var5=var1/6
while var3<var4:
    var6=sin(var3);var7=var3;var3+=var5
    if abs(var6)<var2:continue
    var8=1/var6
    print(f"var7={var7};var6={var6};var8={var8}")

➡️ Que fait-il ?

Vous avez déjà vu ce code !

from math import sin

pi = 3.14159265
epsilon = 1e-6
actuel = -2 * pi
fin = 2 * pi
increment = pi / 6
while actuel < fin:
    sin_actuel = sin(actuel)
    precedent = actuel
    actuel += increment

    if abs(sin_actuel) < epsilon: # si sin(actuel) est proche de 0
        continue                  # évite les instructions suivantes
    
    valeur = 1 / sin_actuel
    print(f"x = {precedent:>9.6f} ;"
        f" sin(x) = {sin_actuel:>9.6f} ;"
        f" 1 / sin(x) = {valeur:>9.6f}")

Le style, ça compte

  • On écrit le code d'abord pour les êtres humains.
  • Ensuite, on écrit le code pour la machine.
  • Le code doit être simple, et si possible, évident.

Maintenance

  • Un développeur passe plus de 80% de son temps à lire du code existant.
  • Des projets de plus de 100 000 lignes de code sont courant.
  • Imaginez-vous devoir lire, comprendre et corriger des problèmes dans un code que vous n'avez pas écrit.
  • Imaginez que le développeur initial a quitté l'équipe, voire l'entreprise...

Une histoire de coûts

  • Un code simple à comprendre prendra moins de temps à faire évoluer.
  • Un code simple comporte en général moins de problèmes.
  • Le temps passé à comprendre du code et corriger des problèmes génère un coût pour les entreprises.
  • La mauvaise qualité d'un logiciel impacte l'image de marque d'une entreprise.

Mais les performances ?

  • Un code difficile à comprendre ne s'exécute pas plus rapidement.
  • En général, 80% du temps d'exécution est passé dans 20% du code.
  • On optimise uniquement les 20%...
  • Même les parties optimisées doivent être maintenables.

Qu'est-ce qu'un code lisible ?

  • Commentaires : ils aident à comprendre les parties non triviales.
  • Variables : elles doivent être bien nommées.
  • Indentation : Python vous y oblige !
  • Espacement :
    • Vertical pour séparer les blocs de code.
    • Horizontal pour séparer les composantes d'une expression.
    • Une expression par ligne.

Commentaires

# Les commentaires commencent par le caractère "#".
# Ils ne sont pas exécutés par l'interpréteur Python.
une_ligne = "peut commencer" # par des instructions
                             # qui s'exécutent et
                             # se terminent par des
                             # commentaires.

Bonnes pratiques

  • L'une des difficultés du développement logiciel est d'écrire du code simple à comprendre.
  • De nombreux outils et techniques visent notamment cet objectif.
  • Il existe de nombreuses autres bonnes pratiques.
  • Nous en mentionnerons quelques unes dans le reste du cours.

Bonnes pratiques en Python

  • Python est un langage piloté par une communauté.
  • La communauté écrit des propositions : Python Enhancement Proposals (PEP).
  • Les propositions sont discutées puis intégrées dans le langage, afin de l'améliorer.
  • L'une des propositions est PEP 8 et discute du style en Python.

PEP 8

Lien vers PEP 8

Grandes lignes dans les prochaines diapositives

Rester consistant

  • La règle la plus importante est de rester consistant.
  • Une base de code écrite par N développeurs doit donner le sentiment qu'elle a été écrite par une seule et même personne.
  • Les règles d'entreprise sont prioritaires sur celles de PEP 8.

Espaces ou Tabulations ?

On utilise 4 espaces pour l'indentation.

Longueur maximale de ligne

Maximum 79 caractères par ligne au total.

Règles pour une expression sur plusieurs lignes

  • 79 caractères, c'est peu.
  • On doit souvent revenir à ligne pour les expressions complexes.
  • Exemple :
revenus = (salaire_net
           + interets_comptes_bancaires
           - impots_revenus
           - remboursements_emprunt)

Nombreuses autres règles...

  • N'hésitez pas à consulter PEP 8.
  • Regardez du code écrit en suivant ces règles, par exemple sur Python.org.

Devoir à la Maison 01

DM : Retours sur Scratch et Python

Lien vers le sujet de DM.

Faisons sifler les serpents !

Cela ne voudrait de toutes façons rien dire. Mieux vaut interdire une syntaxe sans sémantique.

UTF-8 ne permet pas de représenter tous les symboles de toutes les langues. Il existe de très nombreux autres formats d'encodage. Certains formats sont populaires pour certaines langues. Par défaut, Python utilise UTF-8.

Nous reviendrons plus tard sur le type binaire.

Extensions disponibles pour presque tous les langages de programmation. Excellent support de Python. Vous pourrez l'utiliser sans problème pour vos projets professionnels. Est utilisé notamment pour l'édition de ce cours.

Evidemment, on pourrait faire `sum(range(nombre + 1))` si on avait déjà quelques connaissances plus avancées. Mais ici, on se pose la question de l'implémentation de sum.

`i` ne fait que diminuer. `i` reste donc indéfiniment plus petit que `nombre`. La condition de fin de la boucle n'est jamais atteinte.

sin(pi/2) == 0. En partant de -2*pi, et avec un increment de pi/6, on va passer par pi/2 et -pi/2. Donc, on pourrait arriver sur des divisions par zéro. Avec les erreurs numériques, la probabilité de tomber sur une division par zéro est faible. En revanche, on observerait des valeurs immenses en divisant par des nombres proches de zéro. Pratique pour éviter des valeur gênantes sans toutefois arrêter la boucle.

On cherche ici le plus grand nombre inférieur à 1 milliard qui soit divible par 11 et 27. L'instruction break arrête la boucle. Cette instruction permet d'ajouter des conditions de fin au cours de l'exécution d'une itération.

On peut tout faire avec des boucles while. On remarque que pour un cas simple comme celui-ci, on est obligé d'utiliser un index i et de calculer explicitement la borne de fin.

On peut également itérer sur des entiers.

Si on connait la borne supérieure, on peut lister tous les nombres par lesquels on doit passer.

On se retrouve à nouveau coincé : soit on retourne sur des boucles while, soit il nous faut un autre concept pour générer un ensemble d'entiers.

Va de 0 au nombre indiqué moins 1.

Extensions disponibles pour presque tous les langages de programmation. Excellent support de Python. Vous pourrez l'utiliser sans problème pour vos projets professionnels. Est utilisé notamment pour l'édition de ce cours.

Assez similaire finalement. Scratch vous oriente pour empêcher les erreurs de syntaxe. Rechercher les blocs prend beaucoup de temps. Ecrire les algorithmes dans un éditeur de texte est beaucoup plus rapide.

Scratch oriente encore une fois pour empêcher les erreurs de syntaxe. Scratch propose des choses non disponibles par défaut dans Python : attendre une collision par exemple. C'est parce que Scratch est à la fois un langage et une boîte à outils pour le développement de jeux vidéos.

Personnellement, je commencerais par pip install PyGame, qui est une boîte à outils pour créer des jeux vidéos simples en Python. PyGame est plus complexe que Scratch, mais il permet d'aller beaucoup plus loin, par exemple en créant des jeux 3D.

Qu'en déduisez-vous ?

Une mauvaise image de marque implique au final moins de ventes, et donc également un coût.

L'une des questions philosophiques entre programmeurs...