Aller au contenu principal
PythonDevOpsFormationProgrammation

Structures de contrôle et fonctions

30 min de lecture Python DevOps — Chapitre 2

Conditions, boucles for/while, fonctions, et compréhensions de listes en Python. Tout pour écrire tes premiers scripts DevOps.

🎯 Objectif : À la fin de ce chapitre, tu maîtriseras les conditions, boucles, fonctions et compréhensions de listes — les outils pour écrire de vrais scripts d’automatisation. ⏱️ Durée estimée : 45 minutes | Niveau : Débutant-Intermédiaire

Conditions : if, elif, else

Les conditions contrôlent le flux d’exécution. En Python, l’indentation (4 espaces) délimite les blocs — pas d’accolades.

status_code = 503

if status_code == 200:
    print("OK")
elif status_code == 404:
    print("Not Found")
elif status_code >= 500:
    print("Server Error — alerte critique !")
else:
    print(f"Code inattendu : {status_code}")

Combiner des conditions

cpu = 85
memory = 92

if cpu > 80 and memory > 90:
    print("ALERTE : charge critique sur CPU et mémoire")
elif cpu > 80 or memory > 90:
    print("WARNING : une ressource sous pression")

# Vérification de plage (syntaxe Python élégante)
port = 8080
if 1024 <= port <= 65535:
    print("Port valide (non privilégié)")

Opérateur ternaire

Une condition sur une seule ligne, utile pour les assignations simples.

env = "production"
debug = True if env == "development" else False
status = "healthy" if cpu < 80 else "degraded"

💡 L’opérateur ternaire Python se lit comme du français : “debug est True si env est development, sinon False”.

Boucles : for et while

La boucle for

La boucle for parcourt n’importe quelle séquence : liste, string, range, dict…

# Parcourir une liste
servers = ["web-01", "web-02", "db-01"]
for server in servers:
    print(f"Vérification de {server}...")

# Avec range (début, fin, pas)
for i in range(5):              # 0, 1, 2, 3, 4
    print(f"Tentative {i + 1}")

for i in range(0, 100, 10):    # 0, 10, 20, ..., 90
    print(i)

enumerate et zip

Deux outils indispensables pour les boucles Python.

# enumerate : accéder à l'index ET la valeur
services = ["nginx", "postgresql", "redis"]
for index, service in enumerate(services):
    print(f"{index + 1}. {service}")

# zip : parcourir plusieurs listes en parallèle
servers = ["web-01", "web-02", "web-03"]
ips = ["10.0.1.1", "10.0.1.2", "10.0.1.3"]

for server, ip in zip(servers, ips):
    print(f"{server}{ip}")

La boucle while

while s’exécute tant que la condition est vraie. Attention aux boucles infinies.

retries = 0
max_retries = 5
connected = False

while not connected and retries < max_retries:
    print(f"Tentative {retries + 1}...")
    if retries == 2:  # Simulation : succès à la 3e tentative
        connected = True
        print("Connecté !")
    retries += 1

if not connected:
    print("Échec après 5 tentatives")

break et continue

# break : sortir immédiatement de la boucle
for port in range(8000, 9000):
    if port == 8080:
        print(f"Port {port} trouvé !")
        break

# continue : passer à l'itération suivante
logs = ["INFO: started", "ERROR: disk full", "INFO: request", "WARNING: high CPU"]
for log in logs:
    if log.startswith("INFO"):
        continue  # On ignore les logs INFO
    print(f"⚠️  {log}")

🔥 break est particulièrement utile dans les boucles while True pour les retry patterns — tu boucles indéfiniment et tu sors quand la condition de succès est remplie.

Fonctions : organiser et réutiliser

Définir une fonction

def check_disk_usage(path, threshold=80):
    """Vérifie l'utilisation disque et retourne un statut."""
    usage = 75  # Simulation
    if usage > threshold:
        return "CRITICAL", usage
    return "OK", usage

# Appels
status, usage = check_disk_usage("/var/log")
status, usage = check_disk_usage("/data", threshold=90)

💡 La triple-quote sous def est une docstring. Elle documente ce que fait la fonction. C’est une convention Python, pas juste un commentaire.

Types d’arguments

# Positionnels et nommés avec valeurs par défaut
def deploy(service, env="staging", replicas=1):
    print(f"Déploiement de {service} en {env} ({replicas} réplicas)")

deploy("nginx")                         # staging, 1 réplica
deploy("nginx", "production", 3)         # production, 3 réplicas
deploy("nginx", replicas=5, env="prod")  # nommés dans n'importe quel ordre

# *args : nombre variable d'arguments positionnels
def log_event(*messages, level="INFO"):
    for msg in messages:
        print(f"[{level}] {msg}")

log_event("Démarrage", "Config chargée")
log_event("Erreur fatale", level="ERROR")

# **kwargs : nombre variable d'arguments nommés
def create_resource(**config):
    for key, value in config.items():
        print(f"  {key}: {value}")

create_resource(name="web-server", cpu=2, memory="4Gi")

Fonctions lambda

Une fonction anonyme sur une seule ligne. Utile pour les tris et les callbacks.

# Tri personnalisé par CPU usage
servers = [
    {"name": "web-01", "cpu": 85},
    {"name": "web-02", "cpu": 42},
    {"name": "web-03", "cpu": 67},
]
servers.sort(key=lambda s: s["cpu"])
# Résultat : web-02 (42), web-03 (67), web-01 (85)

# Filtre avec filter()
critical = list(filter(lambda s: s["cpu"] > 80, servers))

⚠️ Les lambdas sont limitées à une seule expression. Pour toute logique complexe, utilise une fonction def classique.

Compréhensions de listes

Les compréhensions sont la façon Python de transformer et filtrer des collections en une seule ligne. C’est puissant, concis et lisible.

Syntaxe de base

# Boucle classique
squares = []
for i in range(10):
    squares.append(i ** 2)

# Compréhension (équivalent en 1 ligne)
squares = [i ** 2 for i in range(10)]

Avec filtre

# Filtrer les ports non-privilégiés
ports = [22, 80, 443, 3000, 5432, 8080]
high_ports = [p for p in ports if p > 1024]
# [3000, 5432, 8080]

Exemples DevOps concrets

# Générer des noms de serveurs
servers = [f"web-{i:02d}" for i in range(1, 11)]
# ['web-01', 'web-02', ..., 'web-10']

# Extraire les IPs d'une liste de dicts
nodes = [
    {"name": "node-1", "ip": "10.0.1.1"},
    {"name": "node-2", "ip": "10.0.1.2"},
]
ips = [node["ip"] for node in nodes]

# Filtrer les serveurs en erreur depuis un dict de statuts
statuses = {"web-01": "ok", "web-02": "error", "db-01": "ok"}
errors = [name for name, status in statuses.items() if status == "error"]
# ['web-02']

# Parser des variables d'environnement
env_vars = ["DB_HOST=localhost", "DB_PORT=5432", "DB_NAME=app"]
config = {k: v for k, v in (var.split("=") for var in env_vars)}
# {'DB_HOST': 'localhost', 'DB_PORT': '5432', 'DB_NAME': 'app'}

🎯 Règle : si ta compréhension dépasse une ligne ou devient illisible, reviens à une boucle for classique. La lisibilité prime sur la concision.

Gestion des erreurs : try/except

Les erreurs arrivent : fichier manquant, API timeout, format invalide. try/except permet de les gérer proprement sans crasher le programme.

def read_config(filepath):
    """Lit un fichier de configuration."""
    try:
        with open(filepath) as f:
            return f.read()
    except FileNotFoundError:
        print(f"⚠️ Fichier {filepath} introuvable")
        return None
    except PermissionError:
        print(f"⚠️ Pas les droits sur {filepath}")
        return None

# Avec finally (s'exécute toujours, erreur ou pas)
def connect_db(host, port):
    connection = None
    try:
        connection = create_connection(host, port)
        return connection
    except ConnectionError as e:
        print(f"Échec de connexion : {e}")
        return None
    finally:
        if connection and not connection.is_active:
            connection.close()

💡 Toujours catcher des exceptions spécifiques (FileNotFoundError, ValueError). Un except Exception nu masque les vrais bugs.

Cas entreprise : moniteur de conteneurs

Contexte : tu écris un script qui analyse l’état de conteneurs Docker et génère un rapport.

containers = [
    {"id": "a1b2c3", "name": "nginx", "status": "running", "cpu": 12.5},
    {"id": "d4e5f6", "name": "postgres", "status": "running", "cpu": 45.2},
    {"id": "g7h8i9", "name": "redis", "status": "exited", "cpu": 0.0},
    {"id": "j0k1l2", "name": "worker", "status": "running", "cpu": 88.7},
    {"id": "m3n4o5", "name": "api", "status": "running", "cpu": 67.3},
]

def container_report(containers):
    """Génère un rapport sur les conteneurs."""
    running = [c for c in containers if c["status"] == "running"]
    exited = [c for c in containers if c["status"] == "exited"]

    print(f"📊 Rapport : {len(running)} actifs, {len(exited)} arrêtés\n")

    # Conteneur le plus gourmand
    if running:
        top = max(running, key=lambda c: c["cpu"])
        avg = sum(c["cpu"] for c in running) / len(running)
        print(f"🔥 Plus gourmand : {top['name']} ({top['cpu']}%)")
        print(f"📈 CPU moyen : {avg:.1f}%\n")

    # Détail par conteneur
    for c in containers:
        icon = "🟢" if c["status"] == "running" else "🔴"
        alert = " ⚠️ HIGH" if c["cpu"] > 80 else ""
        print(f"  {icon} {c['name']:10} CPU: {c['cpu']:5.1f}%{alert}")

container_report(containers)

Ce script combine tout : compréhensions, fonctions, conditions, f-strings, lambda dans max().

Les pièges classiques

⚠️ Arguments mutables par défautdef add(item, lst=[]) partage la même liste entre les appels. Utilise lst=None puis lst = lst or [].

⚠️ Boucle infinie — Oublier d’incrémenter le compteur dans un while. Toujours vérifier que la condition de sortie est atteignable.

⚠️ Indentation mélangée — Tabs et espaces ensemble causent des IndentationError. Configure ton éditeur sur 4 espaces, point final.

⚠️ Scope des variables — Une variable définie dans une fonction n’existe pas en dehors. Si tu as besoin de la valeur, retourne-la.

⚠️ except nuexcept: sans type d’exception catche tout, y compris KeyboardInterrupt et SystemExit. Toujours spécifier le type d’exception.

Résumé

Tu as maintenant les outils pour écrire de vrais scripts Python :

  • Conditionsif/elif/else, opérateur ternaire, conditions chaînées
  • Bouclesfor avec range/enumerate/zip, while, break/continue
  • Fonctionsdef, arguments par défaut, *args/**kwargs, lambdas
  • Compréhensions[expr for x in iterable if condition] pour listes, dicts, sets
  • Erreurstry/except/finally avec des exceptions spécifiques

🎯 La combinaison conditions + boucles + fonctions + compréhensions couvre 90% de ce que tu écriras en scripts DevOps. Le prochain chapitre aborde les classes, les modules et la gestion de fichiers pour passer au niveau supérieur.


➡️ La suite : Classes, modules et fichiers

Articles liés