Questions d'entretien Python
Les entretiens Python testent votre compréhension des fonctionnalités principales du langage, des structures de données, des algorithmes et de la programmation orientée objet. Ils incluent souvent des questions conceptuelles pour évaluer la profondeur de vos connaissances, ainsi que des problèmes de codage pratiques pour mesurer vos compétences réelles. Que vous soyez un développeur junior ou senior, maîtriser ces sujets est crucial pour réussir.
Ce que couvrent les entretiens Python
Syntaxe de base et structures de données
Questions sur les types intégrés de Python comme les listes, dictionnaires, ensembles, tuples et la manipulation de chaînes, y compris la complexité temporelle.
Programmation orientée objet (OOP)
Couverture des classes, de l'héritage, du polymorphisme et des méthodes spéciales comme __init__, __str__ et les décorateurs de propriété.
Programmation fonctionnelle et compréhensions
Inclut lambda, map, filter, reduce, les compréhensions de listes/dictionnaires et les expressions génératrices.
Concurrence et performance
Sujets comme le GIL, threading vs multiprocessing, async/await et le profilage.
Exemples de questions d'entretien Python
- Quelles sont les principales fonctionnalités de Python ?Ce qu'une bonne réponse couvre
- Syntaxe claire et lisible
- Typage dynamique et fort
- Gestion automatique de la mémoire
- Grande bibliothèque standard
- Multi-paradigme (procédural, OOP, fonctionnel)
Voir un exemple de réponse
Python est un langage interprété, à typage dynamique et fort. Parmi ses principales fonctionnalités, on retrouve une syntaxe simple favorisant la lisibilité, une gestion automatique de la mémoire via le ramasse-miettes et le comptage de références, une vaste bibliothèque standard couvrant de nombreux domaines (réseau, fichiers, etc.), et le support de multiples paradigmes de programmation. Il est également multiplateforme et dispose d'une grande communauté. Ces caractéristiques en font un outil polyvalent adapté au développement rapide, à l'analyse de données, à l'automatisation, etc.
- Expliquez la différence entre liste et tuple. Quand utiliseriez-vous chacun ?Ce qu'une bonne réponse couvre
- Mutable vs immuable
- Performance mémoire et vitesse
- Cas d'usage typiques
- Hashabilité des tuples
- API similaires mais différences sémantiques
Voir un exemple de réponse
La principale différence est la mutabilité : les listes sont modifiables (append, remove, etc.) tandis que les tuples sont immuables. En termes de performance, les tuples sont légèrement plus rapides et consomment moins de mémoire car leur taille est fixe. Les tuples peuvent être utilisés comme clés de dictionnaire car ils sont hashables, contrairement aux listes. On utilise les listes pour des collections homogènes de taille variable (liste d'articles à traiter) et les tuples pour des structures hétérogènes de taille fixe (coordonnées, enregistrement de base de données). Il faut éviter d'utiliser un tuple pour des données qui nécessitent des modifications fréquentes.
- Comment Python gère-t-il la mémoire ? Discutez du ramasse-miettes et du comptage de références.Ce qu'une bonne réponse couvre
- Comptage de références
- Ramasse-miettes cyclique
- Générations dans le GC
- Déclenchement manuel avec gc.collect()
- Problèmes de références circulaires
Voir un exemple de réponse
Python utilise un comptage de références comme mécanisme principal de gestion mémoire : chaque objet maintient un compteur du nombre de références pointant vers lui ; quand ce compteur atteint zéro, la mémoire est libérée immédiatement. Cependant, pour les références cycliques (deux objets se référençant mutuellement), le comptage de références échoue. Python dispose donc d'un ramasse-miettes (gc) optionnel qui détecte et nettoie ces cycles. Le GC utilise une approche générationnelle (0, 1, 2) pour optimiser les performances. Les développeurs peuvent interagir avec le module gc, par exemple en désactivant le GC ou en forçant une collecte. Des cycles peuvent aussi être évités en utilisant weakref.
- Écrivez une fonction pour trouver le deuxième plus grand nombre dans une liste sans utiliser le tri.Ce qu'une bonne réponse couvre
- Parcours unique de la liste
- Comparaison des valeurs candidates
- Gestion des cas particuliers (moins de 2 éléments)
- Complexité O(n)
- Pas de tri ni d'utilisation de heap
Voir un exemple de réponse
Pour trouver le deuxième plus grand nombre sans trier, on parcourt la liste une fois en conservant les deux plus grandes valeurs. On initialise deux variables (plus_grand et deuxieme) avec des sentinelles (None ou -inf). Pour chaque élément, on met à jour : si l'élément > plus_grand, on déplace plus_grand dans deuxieme puis on assigne l'élément à plus_grand ; sinon si l'élément > deuxieme et différent de plus_grand, on met à jour deuxieme. Attention à gérer les doublons (on veut le deuxième plus grand distinct) et le cas où la liste a moins de 2 éléments (retourner None ou lever une exception). Complexité O(n) en temps, O(1) en espace.
Solution de référencepython def deuxieme_plus_grand(liste): if len(liste) < 2: return None # Initialisation avec -inf pour gérer les nombres négatifs plus_grand = float('-inf') deuxieme = float('-inf') for nombre in liste: if nombre > plus_grand: deuxieme = plus_grand plus_grand = nombre elif nombre > deuxieme and nombre != plus_grand: deuxieme = nombre # Si deuxieme est toujours -inf, il n'y a pas de deuxième distinct if deuxieme == float('-inf'): return None return deuxieme - Implémentez un décorateur qui mesure le temps d'exécution d'une fonction.Ce qu'une bonne réponse couvre
- Décorateur avec fonction interne
- Utilisation de time.perf_counter() pour mesure précise
- Importation de functools.wraps pour préserver les métadonnées
- Gestion des fonctions avec arguments (*args, **kwargs)
- Idéal pour profilage simple
Voir un exemple de réponse
Un décorateur est une fonction qui prend une autre fonction en argument et retourne une fonction modifiée. Pour mesurer le temps, on crée une fonction wrapper qui enregistre l'heure de début (time.perf_counter()), exécute la fonction originale, enregistre l'heure de fin, affiche la durée, puis retourne le résultat. On utilise functools.wraps pour copier les métadonnées (nom, docstring) de la fonction originale vers la wrapper. Le décorateur peut être appliqué avec la syntaxe @chronometre. Attention à bien gérer les exceptions et à utiliser time.perf_counter() plutôt que time.time() pour une meilleure précision.
Solution de référencepython import time from functools import wraps def chronometre(func): @wraps(func) def wrapper(*args, **kwargs): debut = time.perf_counter() resultat = func(*args, **kwargs) fin = time.perf_counter() print(f"{func.__name__} a pris {fin - debut:.4f} secondes") return resultat return wrapper # Exemple d'utilisation @chronometre def somme_liste(n): return sum(range(n)) - Qu'est-ce que le GIL ? Comment affecte-t-il le multithreading ?Ce qu'une bonne réponse couvre
- GIL = Global Interpreter Lock
- Empêche l'exécution simultanée de bytecode Python
- Impact sur le multithreading CPU-bound
- Les threads I/O-bound bénéficient quand même
- Solutions : multiprocessing, asyncio, extension C
Voir un exemple de réponse
Le GIL (Global Interpreter Lock) est un mutex dans l'interpréteur CPython qui empêche l'exécution simultanée de plusieurs threads exécutant du bytecode Python. Cela garantit la sécurité mémoire mais limite les performances des programmes multithreads intensifs en CPU (ils s'exécutent en fait séquentiellement). Pour les tâches I/O-bound (attente réseau, fichiers), les threads peuvent libérer le GIL pendant les opérations d'entrée/sortie, offrant un gain réel. Pour contourner le GIL, on peut utiliser le module multiprocessing (processus séparés avec mémoire partagée), ou asyncio pour la concurrence asynchrone, ou écrire des extensions en C qui peuvent libérer explicitement le GIL.
- Écrivez un générateur qui produit les nombres de Fibonacci jusqu'à n.Ce qu'une bonne réponse couvre
- Utilisation de yield pour produire une séquence
- Génération paresseuse (lazy evaluation)
- Gestion de l'état entre les appels
- Arrêt avec return ou en épuisant le générateur
- Optimisation mémoire pour de grandes séquences
Voir un exemple de réponse
Un générateur en Python utilise yield pour retourner une valeur tout en suspendant l'état de la fonction. Pour Fibonacci, on initialise a=0, b=1, puis dans une boucle while on yield a, puis on met à jour (a, b = b, a+b). On peut ajouter une condition de terminaison basée sur un paramètre n (nombre de termes à générer) ou une valeur maximale. Le générateur permet de produire une séquence infinie ou finie sans stocker tous les éléments en mémoire. On itère dessus avec for ou next(). Attention à bien gérer le cas où n <= 0 : ne rien yield.
Solution de référencepython def fibonacci(n): """Génératrice des nombres de Fibonacci jusqu'au n-ième terme (n >= 0).""" a, b = 0, 1 for _ in range(n): yield a a, b = b, a + b # Exemple d'utilisation for nb in fibonacci(10): print(nb) - Expliquez le MRO (Method Resolution Order) en Python et comment super() fonctionne.Ce qu'une bonne réponse couvre
- MRO = ordre de résolution des méthodes en héritage multiple
- Algorithme C3 linearization
- super() suit le MRO pour appeler la classe suivante
- Permet la coopération entre classes (cooperative multiple inheritance)
- Problèmes potentiels : héritage en diamant
Voir un exemple de réponse
Le MRO (Method Resolution Order) en Python détermine l'ordre dans lequel les classes de base sont recherchées lors de l'appel d'une méthode en héritage multiple. Il suit l'algorithme C3 linearization qui garantit un ordre cohérent et monotone. On peut visualiser le MRO avec className.__mro__. La fonction super() utilise le MRO pour appeler la méthode dans la classe suivante dans la hiérarchie, ce qui permet une coopération entre classes via l'héritage en diamant. Cependant, une mauvaise utilisation de super() dans des hiérarchies complexes peut mener à des appels inattendus ou des boucles. Il est important de concevoir les classes de manière cohérente avec le MRO et d'utiliser super() plutôt que de nommer explicitement les classes parentes.
Comment se préparer
- Maîtrisez les fonctions intégrées et la bibliothèque standard (par exemple, itertools, collections).
- Entraînez-vous sur des plateformes comme LeetCode ou HackerRank en vous concentrant sur des solutions spécifiques à Python.
- Comprenez le modèle mémoire de Python et comment les objets sont passés (référence vs valeur).
- Soyez capable d'expliquer les patrons de conception et les idiomes pythoniques (par exemple, les gestionnaires de contexte, les décorateurs).
- Révisez les structures de données importantes : dictionnaires, ensembles et leurs complexités temporelles.
Questions fréquemment posées
Quelle est la différence entre `==` et `is` ?
`==` vérifie l'égalité de valeur ; `is` vérifie l'identité (même objet). Utilisez `is` pour les singletons comme None.
Comment gérez-vous les exceptions en Python ?
Utilisez les blocs try/except ; optionnellement avec else et finally pour le nettoyage. Évitez les except nus.
Quels sont les espaces de noms et les règles de portée en Python ?
Règle LEGB : Locale, Englobante, Globale, Intégrée. Les variables sont recherchées dans cet ordre.
Qu'est-ce qu'un générateur et en quoi est-il différent d'une liste ?
Les générateurs produisent des éléments paresseusement en utilisant yield ; ils ne stockent pas toutes les valeurs en mémoire, adaptés aux grandes données.
Comment optimiser le code Python pour les performances ?
Utilisez les fonctions intégrées (par exemple, map, filter), les compréhensions de listes, évitez les recherches globales, utilisez des outils de profilage et envisagez des extensions C.
Pratiquez les questions Python avec des retours instantanés de l'IA
Téléchargez votre CV, obtenez un entretien simulé personnalisé et voyez exactement ce qu'il faut améliorer — gratuit pour commencer.