Exercice 01 : Métaclasse pour la Conversion Automatique des Noms de Méthodes
Objectif
Cet exercice a pour but de vous faire créer une métaclasse qui inspecte les méthodes d'une classe et les convertit automatiquement en minuscules (lowercase). Cela illustre comment une métaclasse peut manipuler la structure d'une classe avant sa création finale.
Contexte
Dans certaines conventions de codage ou pour des raisons de compatibilité, il peut être nécessaire de s'assurer que tous les noms de méthodes d'une API suivent un format spécifique, comme être entièrement en minuscules. Une métaclasse est l'outil parfait pour automatiser cette transformation et garantir la cohérence.
Énoncé
-
Créez un nouveau fichier Python nommé
lowercase_meta.py. -
Définissez une métaclasse
LowercaseMethodMeta.- Elle doit hériter de
type. - Surchargez la méthode
__new__(cls, name, bases, attrs).
- Elle doit hériter de
-
Dans la méthode
__new__:- Créez un nouveau dictionnaire, par exemple
new_attrs, pour stocker les attributs modifiés. - Itérez sur les paires clé-valeur (
key,value) du dictionnaireattrsoriginal. - Pour chaque attribut :
- Si la clé (
key) commence par__(ce sont des méthodes spéciales comme__init__), ajoutez-la aunew_attrssans modification. - Si la valeur (
value) est un objet appelable (une fonction/méthode), cela signifie que c'est une méthode que nous voulons transformer. Ajoutez-la aunew_attrsmais avec son nom de clé converti en minuscules (key.lower()). - Pour tous les autres attributs (attributs de classe simples), ajoutez-les au
new_attrssans modification.
- Si la clé (
- Une fois la boucle terminée, appelez la méthode
super().__new__en lui passantnew_attrsà la place deattrspour construire la classe avec les noms de méthodes modifiés. - Retournez la nouvelle classe créée.
- Créez un nouveau dictionnaire, par exemple
-
Créez une classe de test
MyServicequi utilise cette métaclasse.- Définissez plusieurs méthodes avec des noms en casse mixte (CamelCase, PascalCase), par exemple :
ConnectToAPI,FetchData,process_RESULTS. - Chaque méthode doit simplement retourner une chaîne de caractères indiquant son nom original pour le test.
- Définissez plusieurs méthodes avec des noms en casse mixte (CamelCase, PascalCase), par exemple :
-
Testez le comportement.
- Créez une instance de
MyService. - Essayez d'appeler les méthodes en utilisant leurs noms en minuscules (par exemple,
service.connecttoapi()). - Vérifiez que les appels fonctionnent et retournent les bonnes chaînes.
- Utilisez
hasattrpour vérifier que les noms de méthodes originaux (en casse mixte) n'existent plus sur l'instance.
- Créez une instance de
Résultat Attendu
Le script doit s'exécuter sans erreur. Les appels aux méthodes en minuscules doivent réussir, tandis que la vérification de l'existence des méthodes en casse mixte doit renvoyer False.
# Sortie attendue des print
connecttoapi a été appelée
fetchdata a été appelée
process_results a été appelée
L'attribut 'ConnectToAPI' existe-t-il ? False
L'attribut 'connecttoapi' existe-t-il ? True
Cliquez ici pour voir un exemple de code de solution
# lowercase_meta.py
import inspect
class LowercaseMethodMeta(type):
"""
Métaclasse qui convertit tous les noms de méthodes (sauf les spéciales)
d'une classe en minuscules.
"""
def __new__(cls, name, bases, attrs):
print(f"--- Métaclasse : Traitement de la classe '{name}' ---")
new_attrs = {}
for key, value in attrs.items():
if key.startswith("__"):
# Garder les méthodes spéciales intactes
new_attrs[key] = value
elif inspect.isfunction(value):
# Si c'est une fonction, convertir son nom en minuscules
print(f"Conversion de la méthode : '{key}' -> '{key.lower()}'")
new_attrs[key.lower()] = value
else:
# Garder les autres attributs intacts
new_attrs[key] = value
print("--- Métaclasse : Fin du traitement ---")
# Créer la classe avec les attributs modifiés
return super().__new__(cls, name, bases, new_attrs)
# Classe utilisant la métaclasse
class MyService(metaclass=LowercaseMethodMeta):
def ConnectToAPI(self):
return "connecttoapi a été appelée"
def FetchData(self):
return "fetchdata a été appelée"
def process_RESULTS(self):
return "process_results a été appelée"
# --- Tests ---
print("\nCréation de l'instance de service...")
service = MyService()
# Appeler les méthodes avec leurs nouveaux noms en minuscules
print(service.connecttoapi())
print(service.fetchdata())
print(service.process_results())
# Vérifier que les anciens noms n'existent plus
print(f"\nL'attribut 'ConnectToAPI' existe-t-il ? {hasattr(service, 'ConnectToAPI')}")
print(f"L'attribut 'connecttoapi' existe-t-il ? {hasattr(service, 'connecttoapi')}")