Syntaxe, variables, conditions, boucles, fonctions et structures de données — 4 modules pour poser des bases solides
Installer Python, écrire son premier script, nommer des variables et manipuler les types de base.
Python est un langage interprété : le code source (.py) est exécuté ligne par ligne par l'interpréteur, sans étape de compilation. Téléchargez Python 3.12 depuis python.org en cochant « Add python.exe to PATH » sous Windows, puis vérifiez dans un terminal :
python --version
python -m pip --versionprint(), c'est faire bouillir de l'eau pour vérifier que tout fonctionne.La fonction print() affiche du texte à l'écran. Les chaînes de caractères se délimitent par des guillemets simples ou doubles. Le symbole # démarre un commentaire ignoré par l'interpréteur :
print("Bonjour, automatisation !")
print('Adresse IP :', '192.168.1.1')
# Ceci est un commentaire : il documente le code
print("Bonjour, automatisation !") :"Bonjour, automatisation !". État : texte prêt.print(). État affiché : Bonjour, automatisation !# Ceci est un commentaire est ignorée. État : aucun effet, le script continue.python introuvable. Confondre python et python3 selon la machine. Mélanger guillemets simples et doubles dans la même chaîne. Exécuter un autre fichier que celui qu'on vient de modifier.print('IP :', '10.0.0.1')
print("Scan", "terminé")Sortie :IP : 10.0.0.1
Scan terminé
Explication : chaque print() ajoute un retour à la ligne, et la virgule insère une espace entre les arguments.
print().Une variable est un nom qui référence une valeur en mémoire. En Python, on ne déclare pas de type : l'affectation avec = crée la variable et l'interpréteur déduit le type. Les noms utilisent des minuscules et des underscores (snake_case) :
hostname = "SW1"
vlan_id = 10
actif = True
print(hostname, vlan_id, actif)hostname = "SW1" colle l'étiquette « hostname » sur la boîte qui contient « SW1 ». Si tu changes le contenu, l'étiquette suit.On peut affecter plusieurs variables en une ligne, échanger deux valeurs sans variable temporaire, et utiliser les affectations augmentées (+=, -=, *=) très pratiques pour les compteurs :
ip, masque = "10.0.0.1", "255.255.255.0"
compteur = 0
compteur += 1 # compteur vaut 1
compteur *= 10 # compteur vaut 10
compteur = 0 → État : compteur vaut 0.compteur += 1 → État : compteur vaut 1 (0 + 1).compteur *= 10 → État : compteur vaut 10 (1 x 10).ip, masque = "10.0.0.1", "255.255.255.0" → État : ip vaut 10.0.0.1, masque vaut 255.255.255.0.= (affectation) à la place de == (comparaison) dans un test. Mettre des tirets ou des espaces dans un nom (mon-routeur est invalide). Écraser une variable sans s'en rendre compte. Nommer en majuscules ce qui n'est pas une constante.a = 3
b = a
a = 5
print(a, b)Sortie : 5 3.
Explication : b = a copie la valeur 3 au moment de l'affectation. Changer a ensuite ne change pas b.
routeur (« R1 »), interface (« Gi0/0 ») et up (True), puis affichez-les sur une seule ligne avec print().Les quatre types de base couvrent l'essentiel : int (entiers), float (décimaux), str (texte), bool (True/False). La fonction type() révèle le type d'une valeur et les fonctions int(), float(), str() convertissent d'un type à l'autre :
port = 22
bande_passante = 1.5
banniere = "Acces reserve"
print(type(port), type(bande_passante), type(banniere))
print(int("8080") + 1) # 8081 : conversion puis calcul
print(str(404) + " introuvable")"8080" + 1 sans convertir, le script plante en pleine astreinte. Maîtriser les types, c'est éviter ces plantages.int() et str() sont la balance qui convertit.Attention aux conversions impossibles : int("VLAN10") lève une erreur ValueError. En automatisation, les données lues sur un équipement arrivent toujours en texte : il faut les convertir avant de calculer (module 5 et chapitre des exceptions).
port = 22 → type int.type(port) → <class 'int'>.int("8080") + 1 → conversion « 8080 » vers 8080, puis 8080 + 1 = 8081.str(404) + " introuvable" → conversion 404 vers « 404 », puis concaténation : « 404 introuvable ».int("VLAN10") lève ValueError car le texte n'est pas un nombre pur. "3" + 4 lève TypeError : on ne concatène pas du texte et un nombre sans conversion. Les float ont des approximations (0.1 + 0.2). bool("False") vaut True car toute chaîne non vide est vraie.print(int("21") * 2)
print(str(7) + str(7))Sortie :42
77
Explication : int("21") donne l'entier 21, 21 x 2 = 42. str(7) + str(7) concatène deux textes : « 7 » + « 7 » = « 77 », pas 14.
"443" dans port_txt, convertissez-la en entier dans port, ajoutez 1 et affichez le résultat (attendu : 444).Python propose les opérateurs classiques + - * /, plus la division entière //, le modulo % (reste) et la puissance **. Le modulo sert par exemple à tester la parité ou à répartir des adresses :
print(7 / 2) # 3.5 : division reelle
print(7 // 2) # 3 : division entiere
print(7 % 2) # 1 : reste
print(2 ** 8) # 256 : 2 puissance 8 (un /24 = 256 adresses){ }, Python y verse les valeurs.Les f-strings (préfixe f) injectent des variables dans un texte avec des accolades. C'est la façon moderne de générer des configurations et des rapports :
hote = "R1"
vlan = 20
print(f"Switch {hote} : port en VLAN {vlan}")
print(f"Masque /26 : {2 ** (32 - 26) - 2} hotes utilisables")
7 / 2 → 3.5 (division réelle).7 // 2 → 3 (partie entière).7 % 2 → 1 (reste).2 ** 8 → 256.hote = "R1" ; vlan = 20 → f-string : « Switch R1 : port en VLAN 20 »./ et // fausse les calculs de préfixes. Oublier le préfixe f affiche les accolades au lieu des valeurs. Doubler les accolades pour afficher une accolade littérale. Ignorer les priorités : 2 ** (32 - 26) - 2 n'est pas 2 ** (32 - 26 - 2).print(10 % 3)
print(f"{2 ** 4} hotes")Sortie :1
16 hotes
Explication : 10 = 3 x 3 + 1, le reste vaut 1. 2 ** 4 vaut 16, injecté dans la f-string.
prefix = 26, calculez et affichez via une f-string le nombre d'hôtes utilisables d'un /26 (attendu : 62).Prendre des décisions avec if/elif/else et répéter des actions avec for et while — le cœur de tout script.
Une condition exécute un bloc seulement si un test est vrai. Les opérateurs de comparaison sont == != < > <= >= ; on les combine avec and, or et not. Le bloc est délimité par l'indentation (4 espaces) après les deux-points :
tentatives = 15
if tentatives > 10:
print("ALERTE : inviter a verifier cette IP")
elif tentatives > 5:
print("Surveillance renforcee")
else:
print("Trafic normal")En Python, les valeurs « fausses » sont : False, None, 0, "" (chaîne vide), [], {}. On peut donc tester directement if reponse: pour vérifier qu'une chaîne n'est pas vide.
tentatives = 15 :15 > 10 → True, on entre dans le premier bloc.elif et else sont sautés.tentatives = 7 : premier test False, second 7 > 5 True → « Surveillance renforcee ». Avec 2 : les deux False → « Trafic normal ».= au lieu de == dans le test. Oublier les deux-points en fin de ligne if. Indenter avec un mélange de tabulations et d'espaces. Tester if reponse == True au lieu de if reponse, ou mal chaîner and / or.x = 7
if x > 10:
print("haut")
elif x > 5:
print("moyen")
else:
print("bas")Sortie : moyen.
Explication : 7 n'est pas > 10 (False) mais est > 5 (True), donc c'est le bloc elif qui s'exécute.
port = 22 affichez « SSH », si port = 80 affichez « HTTP », sinon affichez « Port inconnu ». Testez avec les trois valeurs.La boucle for parcourt chaque élément d'une séquence. La fonction range(n) génère les entiers de 0 à n-1, range(debut, fin) un intervalle, range(debut, fin, pas) avec un pas personnalisé :
for i in range(5):
print(f"Tentative {i + 1}/5")
for octet in range(1, 6):
print(f"Scan de 192.168.1.{octet}")for fait le même travail en quelques secondes, sans faute de frappe.On peut aussi parcourir directement une liste de chaînes — idéal pour traiter une liste d'équipements ou d'adresses :
equipements = ["R1", "SW1", "SW2"]
for hote in equipements:
print(f"Connexion a {hote}...")
for i in range(5) :range(5) n'a produit que 0 à 4.range(1, 6) produit 1, 2, 3, 4, 5 : la borne de fin est exclue.range(5) commence à 0, pas à 1. La borne de fin est exclue : range(1, 10) s'arrête à 9. Une indentation fausse sort une ligne de la boucle. Modifier une liste pendant qu'on la parcourt saute des éléments.for i in range(3):
print(i)Sortie :0
1
2
Explication : range(3) produit 0, 1, 2. Il n'y a jamais de 3.
for et range().La boucle while répète un bloc tant qu'une condition est vraie. Elle sert quand on ne connaît pas à l'avance le nombre d'itérations (attente d'un état, nouvelles tentatives). Il faut toujours faire évoluer la condition, sinon la boucle est infinie :
essais = 0
while essais < 3:
print(f"Essai {essais + 1}")
essais += 1
print("Fin des essais")while réessaie jusqu'au succès, break stoppe dès qu'un port critique répond.while), avec le droit de partir tout de suite si on entend l'alarme (break) ou de sauter une porte condamnée (continue).Les instructions break (sortir immédiatement) et continue (sauter à l'itération suivante) contrôlent finement les deux types de boucles :
for code in [200, 301, 404, 500, 200]:
if code == 500:
print("Erreur serveur : arret")
break
if code == 301:
continue # on ignore les redirections
print(f"Code traite : {code}")
essais = 0 → test 0 < 3 True → affiche « Essai 1 », essais passe à 1 → test True → « Essai 2 », essais = 2 → test True → « Essai 3 », essais = 3 → test 3 < 3 False → sortie, affiche « Fin des essais ».break : codes 200 traité, 301 sauté par continue, 404 traité, 500 → message puis sortie immédiate, le dernier 200 n'est jamais traité.essais += 1 crée une boucle infinie. Inverser break et continue. Mettre un while True sans aucune porte de sortie. Comparer avec = dans la condition du while.n = 3
while n > 0:
print(n)
n -= 1Sortie :3
2
1
Explication : tant que n > 0, on affiche puis on décrémente. Quand n vaut 0, le test devient False et la boucle s'arrête.
while, affichez un compte à rebours de 5 à 1 puis « Décollage du scan ! ». Utilisez break pour stopper une boucle for sur les ports [22, 80, 443, 3389] dès que le port 443 est atteint.Découper son code en fonctions réutilisables : paramètres, valeurs de retour, portée des variables.
Une fonction est un bloc nommé qui effectue une tâche. On la définit avec def, on l'appelle par son nom suivi de parenthèses. Les paramètres sont les valeurs reçues entre parenthèses :
def saluer(nom):
print(f"Bonjour {nom} !")
saluer("Alice")
saluer("Bob")nom), elle sort toujours le même café formaté, sans refaire la recette.Une fonction peut avoir des paramètres avec valeurs par défaut : l'appelant peut les omettre. La docstring (chaîne sous le def) documente le rôle de la fonction :
def generer_interface(nom, vlan=10):
"""Genere une ligne de configuration de port."""
print(f"interface {nom} : access VLAN {vlan}")
generer_interface("Fa0/1")
generer_interface("Fa0/2", 20)
def saluer(nom) → définit la machine, rien ne s'affiche.saluer("Alice") → paramètre nom = « Alice » → affiche « Bonjour Alice ! ».saluer("Bob") → nom = « Bob » → « Bonjour Bob ! ».generer_interface("Fa0/1") → vlan garde son défaut 10 → « interface Fa0/1 : access VLAN 10 ».saluer au lieu de saluer("Alice")) ne fait rien. Oublier les deux-points après def. Inverser l'ordre des paramètres. Croire que la fonction s'exécute à sa définition : elle ne s'exécute qu'à l'appel.def banniere(hote):
print(f"=== {hote} ===")
banniere("R1")
banniere("SW1")Sortie :=== R1 ===
=== SW1 ===
Explication : chaque appel rejoue le même bloc avec une valeur différente de hote.
banniere(hote) qui affiche « === Configuration de {hote} === » et appelez-la pour « R1 » et « SW1 ».L'instruction return renvoie un résultat à l'appelant et termine la fonction. Sans return, une fonction renvoie None. Séparer le calcul (fonction qui retourne) de l'affichage (print) rend le code testable et réutilisable :
def hotes_utilisables(prefix):
return 2 ** (32 - prefix) - 2
print(hotes_utilisables(24)) # 254
print(hotes_utilisables(26)) # 62return rend la fonction réutilisable et testable au lieu de l'enfermer dans un print.print, c'est crier le résultat par la fenêtre. Le return, c'est rendre la monnaie au client : il peut s'en servir pour la suite.Une fonction peut retourner plusieurs valeurs sous forme de tuple, et retourner tôt pour les cas particuliers (garde) :
def analyser_code(code):
if code == 200:
return "OK", True
if code == 404:
return "Introuvable", False
return "Autre", False
message, succes = analyser_code(404)
print(message, succes)
hotes_utilisables(24) → prefix = 24 → 2 ** (32 - 24) - 2 = 256 - 2 = 254 → la fonction renvoie 254, et print() l'affiche.analyser_code(404) → premier test False, second True → renvoie le tuple ("Introuvable", False) → déballé en message = « Introuvable », succes = False.return : la fonction renvoie None. Mettre return trop tôt dans une boucle : seul le premier élément est traité. Confondre afficher et renvoyer. Déballer le mauvais nombre de valeurs d'un tuple.def f(a):
return a + 1
print(f(41))Sortie : 42.
Explication : f(41) renvoie 42 à l'appelant, et print() affiche cette valeur retournée.
est_prive(ip) qui retourne True si l'adresse commence par « 192.168. » ou « 10. », False sinon. Testez avec « 192.168.1.5 » et « 8.8.8.8 ».Une variable créée dans une fonction est locale : elle n'existe pas à l'extérieur. Une variable créée au niveau du script est globale et lisible partout. On évite de modifier des globales depuis une fonction : on préfère passer des paramètres et retourner des résultats :
seuil = 10 # globale : lisible dans les fonctions
def alerter(ip, tentatives):
message = f"{ip} suspect" # locale : invisible dehors
return tentatives > seuil and message or "OK"extraire_ip, generer_rapport).seuil = 10 (globale). Appel alerter("5.6.7.8", 42) → paramètres ip et tentatives créés en local, message créé en local, lecture de seuil autorisée → test 42 > 10 True → renvoie « 5.6.7.8 suspect ». Après l'appel, message n'existe plus dehors.global crée en fait une locale et peut lever UnboundLocalError. Abuser de global rend le code fragile. Donner à une locale le même nom qu'une globale masque la globale. Lire une locale hors de sa fonction lève NameError.x = 10
def f():
x = 5
return x
print(x)
print(f())
print(x)Sortie :10
5
10
Explication : le x dans la fonction est local et ne touche pas au x global, qui reste à 10 avant comme après l'appel.
classer_tentatives(n) qui retourne « critique » si n > 50, « suspect » si n > 10, « normal » sinon. Testez avec 3, 25 et 100.Listes, tuples, sets, dictionnaires et compréhensions : stocker et transformer des collections efficacement.
Une liste stocke plusieurs valeurs entre crochets. Les éléments sont indexés à partir de 0, les index négatifs partent de la fin. Les méthodes append(), remove(), sort() et la fonction len() sont incontournables :
ports = [22, 80, 443]
ports.append(8080)
print(ports[0], ports[-1], len(ports))
for p in ports:
print(f"Scan du port {p}")len() te dit combien de cases sont remplies.Le slicing liste[debut:fin] extrait une sous-liste, et l'opérateur in teste la présence d'un élément :
logs = ["OK", "OK", "FAIL", "OK"]
print(logs[1:3]) # ['OK', 'FAIL']
print("FAIL" in logs) # True
ports = [22, 80, 443] → 3 éléments.ports.append(8080) → [22, 80, 443, 8080].ports[0] → 22, ports[-1] → 8080, len(ports) → 4.logs[1:3] sur [« OK », « OK », « FAIL », « OK »] → [« OK », « FAIL »] (index 1 inclus, 3 exclu).ports[4] sur 4 éléments lève IndexError. remove(x) sur une valeur absente lève ValueError. b = a ne copie pas la liste, les deux noms partagent la même (utilise a.copy()). Le slicing ne modifie pas la liste d'origine.a = [1, 2, 3]
print(a[1:])
print(len(a))Sortie :[2, 3]
3
Explication : a[1:] part de l'index 1 jusqu'à la fin, et la longueur reste 3.
ips = ["10.0.0.1", "10.0.0.2"], ajoutez « 10.0.0.3 », affichez sa longueur puis chaque adresse avec une boucle.Un tuple (parenthèses) est une liste immuable : parfait pour des valeurs fixes comme une adresse et son masque. Un set (accolades) ne contient que des valeurs uniques et supporte les opérations d'ensembles :
reseau = ("192.168.1.0", 24) # tuple : on ne le modifiera pas
print(f"{reseau[0]}/{reseau[1]}")
vues_jour1 = {"10.0.0.5", "10.0.0.9", "10.0.0.5"}
print(vues_jour1) # {'10.0.0.5', '10.0.0.9'} : doublon elimineLes sets excellent pour comparer des collections : intersection (&), différence (-), union (|). En SOC, on les utilise pour croiser des IP observées avec une liste d'IOC :
observees = {"1.2.3.4", "5.6.7.8", "9.9.9.9"}
ioc = {"5.6.7.8", "9.9.9.9"}
print(observees & ioc) # IP malveillantes detectees
reseau = ("192.168.1.0", 24) → reseau[0] = 192.168.1.0, reseau[1] = 24.{"10.0.0.5", "10.0.0.9", "10.0.0.5"} → le doublon est éliminé → {« 10.0.0.5 », « 10.0.0.9 »}.observees & ioc → garde uniquement les IP présentes des deux côtés : {« 5.6.7.8 », « 9.9.9.9 »}.(x,) avec la virgule, sinon ce sont juste des parenthèses. Un set n'a pas d'ordre : ne compte pas sur l'ordre d'affichage. On ne peut pas mettre une liste (modifiable) dans un set. Modifier un tuple lève TypeError.s = {1, 2, 2, 3}
print(len(s))
print(sorted(s))Sortie :3
[1, 2, 3]
Explication : le doublon 2 est éliminé, il reste 3 valeurs uniques, triées par sorted().
tentatives = ["1.1.1.1", "2.2.2.2", "1.1.1.1"], construisez un set et affichez le nombre d'adresses uniques (attendu : 2).Un dictionnaire associe des clés à des valeurs entre accolades. C'est LA structure de l'automatisation : configs d'équipements, réponses d'API JSON, compteurs. On accède avec d[cle] (ou d.get(cle, defaut) sans erreur si absent) :
equipement = {"hostname": "R1", "ip": "10.0.0.1", "os": "IOS"}
print(equipement["hostname"])
equipement["vlan"] = 10
for cle, valeur in equipement.items():
print(f"{cle} : {valeur}")Les méthodes utiles : .keys(), .values(), .items() pour parcourir, .get() pour un accès sûr, in pour tester une clé. On peut imbriquer listes et dictionnaires pour modéliser un parc entier :
parc = [
{"hostname": "R1", "ip": "10.0.0.1"},
{"hostname": "SW1", "ip": "10.0.0.2"},
]
for eq in parc:
print(f"{eq['hostname']} -> {eq['ip']}")
equipement = {"hostname": "R1", "ip": "10.0.0.1", "os": "IOS"} → equipement["hostname"] = « R1 ».equipement["vlan"] = 10 → ajoute un casier. État : 4 paires.for cle, valeur in equipement.items() → affiche chaque étiquette et son contenu, une ligne par paire.d[cle] lève KeyError : préfère d.get(cle, defaut). Les clés doivent être uniques : un doublon écrase le précédent. Oublier les guillemets autour d'une clé chaîne. Confondre eq['ip'] et eq["ip"] dans une f-string mal quotée.d = {"a": 1, "b": 2}
print(d.get("c", 0))
print(sorted(d.keys()))Sortie :0
['a', 'b']
Explication : la clé « c » est absente donc get() renvoie le défaut 0 sans erreur. Les clés existantes sont « a » et « b ».
alerte = {"ip": "5.6.7.8", "tentatives": 42}, ajoutez la clé « niveau » valant « critique », puis affichez chaque paire clé-valeur.Les compréhensions construisent une collection en une ligne : [expression for x in iterable if condition]. C'est l'idiome Python pour filtrer et transformer des données :
ports = [22, 80, 443, 8080, 3389]
ouverts = [p for p in ports if p < 1024]
print(ouverts) # [22, 80, 443]
lignes = ["OK", "FAIL", "OK", "TIMEOUT"]
erreurs = [l for l in lignes if l != "OK"]
print(erreurs)if) tombent transformées (l'expression) dans le nouveau récipient.Même principe pour les dictionnaires et les sets. On combine souvent compréhension + split() pour parser du texte :
texte = "R1,SW1,SW2"
hotetes = [h.strip() for h in texte.split(",")]
compteurs = {h: 0 for h in hotetes}
print(compteurs) # {'R1': 0, 'SW1': 0, 'SW2': 0}
ports = [22, 80, 443, 8080, 3389] :"R1,SW1,SW2".split(",") → [« R1 », « SW1 », « SW2 »] → {h: 0 for h in ...} → {« R1 »: 0, « SW1 »: 0, « SW2 »: 0}.if avant for) est une erreur de syntaxe. Empiler trois conditions dans une compréhension la rend illisible : repasse à une boucle. En HTML, < dans le code doit rester échappé, sinon le navigateur le mange. Oublier .strip() après split() laisse des espaces parasites.print([x * 2 for x in [1, 2, 3] if x > 1])Sortie : [4, 6].
Explication : 1 est rejeté par le filtre, 2 et 3 sont gardés puis doublés : 2 x 2 = 4, 3 x 2 = 6.
codes = [200, 404, 200, 500, 301, 500], construisez par compréhension la liste des codes supérieurs ou égaux à 400 (attendu : [404, 500, 500]).