Python Intermédiaire : Structures de Données, Fonctions et POO
Expliquez la différence entre une liste et un tuple en Python.
Réponse :
Les listes sont mutables, ce qui signifie que leurs éléments peuvent être modifiés après leur création, et sont définies à l'aide de crochets []. Les tuples sont immuables, ce qui signifie que leurs éléments ne peuvent pas être modifiés, et sont définis à l'aide de parenthèses (). Les tuples sont généralement plus rapides et peuvent être utilisés comme clés de dictionnaire.
Qu'est-ce qu'une compréhension de dictionnaire (dictionary comprehension) ? Fournissez un exemple.
Réponse :
Une compréhension de dictionnaire est un moyen concis de créer des dictionnaires. Elle se compose d'une expression suivie d'une clause for, puis de zéro ou plusieurs clauses for ou if. Par exemple : squares = {x: x*x for x in range(5)} crée {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}.
Quel est le but de *args et **kwargs dans les définitions de fonctions ?
Réponse :
*args permet à une fonction d'accepter un nombre arbitraire d'arguments positionnels, qui sont collectés dans un tuple. **kwargs permet à une fonction d'accepter un nombre arbitraire d'arguments nommés (keyword arguments), qui sont collectés dans un dictionnaire. Ils permettent des signatures de fonctions flexibles.
Expliquez le concept de décorateur en Python.
Réponse :
Un décorateur est un modèle de conception qui vous permet de modifier ou d'étendre la fonctionnalité d'une fonction ou d'une méthode sans modifier explicitement son code source. C'est essentiellement une fonction qui prend une autre fonction comme argument, ajoute une certaine fonctionnalité et retourne une nouvelle fonction. Ils sont couramment utilisés pour la journalisation (logging), la mesure du temps (timing) ou le contrôle d'accès.
Quelle est la différence entre les méthodes __init__ et __new__ dans les classes Python ?
Réponse :
__new__ est une méthode statique responsable de la création et du retour d'une nouvelle instance de la classe avant que __init__ ne soit appelée. __init__ est une méthode d'instance qui initialise l'objet nouvellement créé. __new__ est rarement redéfinie, sauf si vous avez besoin de contrôler la création de l'objet elle-même, comme pour les singletons.
Décrivez le 'method overriding' (redéfinition de méthode) et le 'method overloading' (surcharge de méthode) en Python.
Réponse :
Le 'method overriding' se produit lorsqu'une sous-classe fournit une implémentation spécifique pour une méthode déjà définie dans sa superclasse. Python ne prend pas en charge le 'method overloading' traditionnel (plusieurs méthodes portant le même nom mais avec des paramètres différents) directement ; à la place, vous pouvez utiliser des arguments par défaut ou *args/**kwargs pour obtenir une flexibilité similaire.
Qu'est-ce qu'un générateur en Python et pourquoi l'utiliser ?
Réponse :
Un générateur est une fonction qui retourne un itérateur produisant une séquence de résultats un par un à l'aide du mot-clé yield, au lieu de retourner une seule valeur. Ils sont économes en mémoire car ils ne stockent pas toute la séquence en mémoire, ce qui les rend idéaux pour les grands ensembles de données ou les séquences infinies.
Expliquez le Global Interpreter Lock (GIL) en Python.
Réponse :
Le GIL est un mutex qui protège l'accès aux objets Python, empêchant plusieurs threads natifs d'exécuter simultanément des bytecodes Python. Cela signifie que même sur des processeurs multi-cœurs, un seul thread peut exécuter du bytecode Python à un moment donné. Il simplifie la gestion de la mémoire mais peut limiter la véritable exécution parallèle pour les tâches liées au CPU.
Quel est le but de super() en Python ?
Réponse :
super() est utilisé pour appeler une méthode d'une classe parente ou sœur. Il permet d'accéder aux méthodes héritées qui ont été redéfinies dans une sous-classe, assurant un ordre de résolution des méthodes (MRO - Method Resolution Order) correct dans des hiérarchies d'héritage complexes. Il est couramment utilisé dans les méthodes __init__ des sous-classes.
Réponse :
Les exceptions sont gérées à l'aide des blocs try, except, else et finally. Le bloc try contient le code qui pourrait lever une exception. except intercepte des exceptions spécifiques. else s'exécute si aucune exception ne se produit, et finally s'exécute toujours, qu'une exception se soit produite ou non. Exemple : try: 1/0 except ZeroDivisionError: print('Cannot divide by zero').
Quelle est la différence entre une copie superficielle (shallow copy) et une copie profonde (deep copy) ?
Réponse :
Une copie superficielle crée un nouvel objet composé, puis y insère des références aux objets trouvés dans l'original. Si l'original contient des objets mutables, les modifications apportées à ces objets seront reflétées dans la copie superficielle. Une copie profonde crée un nouvel objet composé, puis y insère récursivement des copies des objets trouvés dans l'original, assurant une indépendance complète.
Expliquez le concept de gestionnaires de contexte (context managers) et l'instruction with.
Réponse :
Les gestionnaires de contexte fournissent un moyen propre de gérer les ressources, en s'assurant que les opérations de configuration et de nettoyage sont correctement gérées, même en cas d'erreurs. L'instruction with est utilisée pour gérer automatiquement l'acquisition et la libération des ressources. Les utilisations courantes incluent la gestion de fichiers, les connexions de base de données et les verrous, garantissant que les ressources sont correctement fermées.