Cours Python · Module 3: Programmation orientée objet

Décorateurs - s'adapter à l'environnement

15 min de lecture
Dans cette leçon12

Bienvenue dans la dernière leçon du module 3 ! Darwin ici, pour une leçon finale avancée.

Dans la nature, les animaux s'adaptent à leur environnement - les caméléons acquièrent la capacité de changer de couleur, les chauves-souris développent l'écholocation, les oiseaux migrateurs développent une navigation magnétique. En Python, les décorateurs permettent une adaptation similaire - ils modifient le comportement des fonctions et des classes sans changer leur code !

1# Fonction ordinaire
2def observe_species(name):
3    print(f"Observation : {name}")
4
5# Fonction avec décorateur - journalisation automatique !
6@log_observation
7def observe_species(name):
8    print(f"Observation : {name}")

Le corps de la fonction n'a pas changé. Une seule ligne, @log_observation, au-dessus de la définition suffit pour que chaque appel soit désormais consigné automatiquement. Tu verras dans un instant comment écrire un tel décorateur.

Que sont les décorateurs ?

Un décorateur est une fonction qui prend une autre fonction (ou classe) et renvoie une version modifiée.

Analogie Safari : c'est comme équiper un chercheur - des jumelles ne changent pas le chercheur, mais elles étendent ses capacités d'observation !

1def my_decorator(func):
2    """Décorateur - prend une fonction, renvoie une version modifiée"""
3    def wrapper():
4        print("Quelque chose avant la fonction")
5        func()  # Appelle la fonction d'origine
6        print("Quelque chose après la fonction")
7    return wrapper
8
9@my_decorator
10def say_hello():
11    print("Bonjour !")
12
13say_hello()

L'appel say_hello() affiche :

1Quelque chose avant la fonction
2Bonjour !
3Quelque chose après la fonction

La ligne du milieu vient de la fonction d'origine, les deux autres du wrapper que le décorateur a mis à la place de say_hello.

La syntaxe @decorator est un raccourci pour :

1def say_hello():
2    print("Bonjour !")
3
4say_hello = my_decorator(say_hello)  # Décoration manuelle

Comment fonctionnent les décorateurs ?

Un décorateur est une fonction d'ordre supérieur - une fonction qui opère sur des fonctions !

1def simple_decorator(func):
2    """
3    Décorateur - motif wrapper
4
5    1. Accepte une fonction
6    2. Définit wrapper
7    3. Renvoie wrapper
8    """
9    def wrapper():
10        print(f"[AVANT] Appel de {func.__name__}")
11        result = func()
12        print(f"[APRÈS] Terminé {func.__name__}")
13        return result
14    return wrapper
15
16@simple_decorator
17def greet():
18    print("Bonjour !")
19    return "Terminé"
20
21# greet est maintenant wrapper, pas la fonction d'origine !
22greet()

Le programme affiche :

1[AVANT] Appel de greet
2Bonjour !
3[APRÈS] Terminé greet

Le wrapper renvoie aussi le résultat de la fonction d'origine (result), donc la valeur "Terminé" ne se perd pas en chemin.

Décorateurs avec arguments de fonction

Pour que le décorateur fonctionne avec des fonctions qui acceptent des arguments, utilise *args et **kwargs :

1def log_decorator(func):
2    def wrapper(*args, **kwargs):
3        """Le wrapper accepte n'importe quels arguments"""
4        print(f"Appel de {func.__name__} avec args={args}, kwargs={kwargs}")
5        result = func(*args, **kwargs)
6        print(f"Résultat : {result}")
7        return result
8    return wrapper
9
10@log_decorator
11def add(a, b):
12    return a + b
13
14@log_decorator
15def greet(name, greeting="Bonjour"):
16    return f"{greeting}, {name} !"
17
18add(5, 3)
19
20greet("Darwin", greeting="Salut")

Les deux appels affichent :

1Appel de add avec args=(5, 3), kwargs={}
2Résultat : 8
3Appel de greet avec args=('Darwin',), kwargs={'greeting': 'Salut'}
4Résultat : Salut, Darwin !

Le même wrapper a géré une fonction avec deux arguments positionnels et une fonction avec un argument nommé : *args recueille les arguments positionnels et **kwargs les arguments nommés.

functools.wraps - préserver les métadonnées

Problème : le décorateur change le __name__ et le __doc__ de la fonction !

1def my_decorator(func):
2    def wrapper(*args, **kwargs):
3        return func(*args, **kwargs)
4    return wrapper
5
6@my_decorator
7def important_function():
8    """Cette fonction est importante"""
9    pass
10
11print(important_function.__name__)  # "wrapper" - faux !
12print(important_function.__doc__)   # None - pas de documentation !

Solution : utilise @functools.wraps !

1from functools import wraps
2
3def my_decorator(func):
4    @wraps(func)  # Préserve les métadonnées de la fonction d'origine
5    def wrapper(*args, **kwargs):
6        return func(*args, **kwargs)
7    return wrapper
8
9@my_decorator
10def important_function():
11    """Cette fonction est importante"""
12    pass
13
14print(important_function.__name__)  # "important_function" - correct !
15print(important_function.__doc__)   # "Cette fonction est importante" - préservée !

IMPORTANT : utilise toujours @wraps(func) dans tes décorateurs !

Décorateurs avec paramètres

Pour qu'un décorateur accepte des paramètres, il faut trois niveaux de fonctions :

1def repeat(times):
2    """Décorateur avec paramètre - répète la fonction N fois"""
3    def decorator(func):
4        @wraps(func)
5        def wrapper(*args, **kwargs):
6            for i in range(times):
7                result = func(*args, **kwargs)
8            return result
9        return wrapper
10    return decorator
11
12@repeat(times=3)  # Paramètre du décorateur !
13def say_hello():
14    print("Bonjour !")
15
16say_hello()

Un seul appel à say_hello() affiche maintenant Bonjour ! trois fois, car le wrapper exécute la fonction d'origine dans une boucle for autant de fois que la valeur de times.

Structure :

  1. repeat(times) - renvoie le décorateur
  2. decorator(func) - le décorateur proprement dit
  3. wrapper(*args, **kwargs) - le wrapper de la fonction

Décorateurs intégrés

Tu connais déjà certains décorateurs intégrés de Python !

@property, @classmethod, @staticmethod

Tu connais les trois depuis les leçons précédentes. Ce sont des décorateurs ordinaires intégrés à Python : chacun change la façon dont on appelle la méthode placée en dessous.

1class Species:
2    def __init__(self, name, population):
3        self._name = name
4        self._population = population
5
6    @property  # Getter
7    def name(self):
8        return self._name
9
10    @property
11    def population(self):
12        return self._population
13
14    @population.setter  # Setter
15    def population(self, value):
16        if value < 0:
17            raise ValueError("La population ne peut pas être négative")
18        self._population = value
19
20    @classmethod  # Méthode de classe
21    def from_dict(cls, data):
22        return cls(data['name'], data['population'])
23
24    @staticmethod  # Méthode statique
25    def is_valid_name(name):
26        return len(name) > 0

Exemples pratiques de décorateurs

1. Décorateur de chronométrage - mesurer le temps d'exécution

Ce décorateur note l'heure avant et après l'appel et affiche la différence en secondes. La fonction mesurée n'a besoin d'aucune modification :

1import time
2from functools import wraps
3
4def timer(func):
5    """Mesure le temps d'exécution de la fonction"""
6    @wraps(func)
7    def wrapper(*args, **kwargs):
8        start = time.time()
9        result = func(*args, **kwargs)
10        end = time.time()
11        print(f"⏱ {func.__name__} a pris {end - start:.4f}s")
12        return result
13    return wrapper
14
15@timer
16def analyze_dna_sequence(sequence):
17    """Simule une analyse ADN - opération chronophage"""
18    time.sleep(2)  # Simulation
19    return f"{len(sequence)} nucléotides analysés"
20
21result = analyze_dna_sequence("ATCGATCG")  # ⏱ analyze_dna_sequence a pris 2.0015s

2. Décorateur logger - journalisation automatique

Le décorateur de journalisation affiche la date et l'heure, le nom de la fonction et les arguments de chaque appel, puis le résultat ou l'erreur. Il n'avale pas l'exception, il la transmet avec raise :

1from functools import wraps
2from datetime import datetime
3
4def log_calls(func):
5    """Consigne chaque appel de fonction"""
6    @wraps(func)
7    def wrapper(*args, **kwargs):
8        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
9        print(f"[{timestamp}] Appel : {func.__name__}")
10        print(f"  Arguments : args={args}, kwargs={kwargs}")
11
12        try:
13            result = func(*args, **kwargs)
14            print(f"  Succès : {result}")
15            return result
16        except Exception as e:
17            print(f"  Erreur : {e}")
18            raise
19    return wrapper
20
21@log_calls
22def observe_species(name, location, count):
23    """Enregistre une observation d'espèce"""
24    if count < 0:
25        raise ValueError("Le nombre ne peut pas être négatif")
26    return f"Enregistré : {count}x {name} à {location}"
27
28observe_species("Lion", "Serengeti", 12)

La console affiche (avec la date et l'heure du moment) :

1[2024-01-15 14:30:45] Appel : observe_species
2  Arguments : args=('Lion', 'Serengeti', 12), kwargs={}
3  Succès : Enregistré : 12x Lion à Serengeti

Si la fonction levait une erreur, par exemple pour un nombre négatif, tu verrais une ligne Erreur au lieu de Succès, et l'exception remonterait jusqu'au code appelant.

3. Décorateur de cache - mémoïsation

Le décorateur de cache mémorise le résultat pour chaque jeu d'arguments. Lors d'un appel suivant avec les mêmes arguments, il renvoie la valeur stockée au lieu de recalculer :

1from functools import wraps
2
3def cache(func):
4    """Met en cache les résultats - ne recalcule pas deux fois la même chose"""
5    cached_results = {}
6
7    @wraps(func)
8    def wrapper(*args):
9        if args in cached_results:
10            print(f"Cache hit pour {args}")
11            return cached_results[args]
12
13        print(f"Calcul pour {args}")
14        result = func(*args)
15        cached_results[args] = result
16        return result
17
18    return wrapper
19
20@cache
21def fibonacci(n):
22    """Calcule le n-ième nombre de Fibonacci"""
23    if n < 2:
24        return n
25    return fibonacci(n - 1) + fibonacci(n - 2)
26
27print(fibonacci(5))

Le programme affiche :

1Calcul pour (5,)
2Calcul pour (4,)
3Calcul pour (3,)
4Calcul pour (2,)
5Calcul pour (1,)
6Calcul pour (0,)
7Cache hit pour (1,)
8Cache hit pour (2,)
9Cache hit pour (3,)
105

Chaque valeur de 0 à 5 n'est calculée qu'une fois, et les seconds appels pour 1, 2 et 3 prennent le résultat dans le cache. Sans le décorateur, la même fonction s'exécuterait 15 fois, et pour un n plus grand, le nombre d'appels explose.

Remarque : Python a un @functools.lru_cache intégré !

1from functools import lru_cache
2
3@lru_cache(maxsize=128)  # Se souvient des 128 derniers résultats
4def fibonacci(n):
5    if n < 2:
6        return n
7    return fibonacci(n - 1) + fibonacci(n - 2)

4. Décorateur de contrôle d'accès - autorisation

Ce décorateur prend un paramètre, le code attendu, et vérifie le premier argument de l'appel. La fonction ne reçoit les autres arguments que si le code est correct :

1from functools import wraps
2
3def require_authorization(authorization_code):
4    """Décorateur avec paramètre - nécessite une autorisation"""
5    def decorator(func):
6        @wraps(func)
7        def wrapper(*args, **kwargs):
8            # Vérifie si le premier argument est un code valide
9            if len(args) == 0 or args[0] != authorization_code:
10                raise PermissionError(f"Accès non autorisé à {func.__name__}")
11
12            # Retire le code d'autorisation des arguments
13            return func(*args[1:], **kwargs)
14        return wrapper
15    return decorator
16
17@require_authorization("SAFARI_2024")
18def access_classified_data(species_name):
19    """Accès aux données classifiées - nécessite une autorisation"""
20    return f"Données classifiées sur {species_name} : [CLASSIFIÉ]"
21
22# Autorisation correcte
23print(access_classified_data("SAFARI_2024", "Rhinocéros noir"))
24# "Données classifiées sur Rhinocéros noir : [CLASSIFIÉ]"
25
26# Sans autorisation - erreur !
27try:
28    print(access_classified_data("WRONG_CODE", "Rhinocéros noir"))
29except PermissionError as e:
30    print(f"Erreur : {e}")

5. Décorateur de réessai - réessai automatique

La connexion à une station de recherche éloignée coupe parfois. Le décorateur de réessai attrape l'exception, attend un peu et réessaie jusqu'à épuiser le nombre de tentatives :

1from functools import wraps
2import time
3
4def retry(max_attempts=3, delay=1):
5    """Réessaie l'opération en cas d'erreur"""
6    def decorator(func):
7        @wraps(func)
8        def wrapper(*args, **kwargs):
9            for attempt in range(1, max_attempts + 1):
10                try:
11                    return func(*args, **kwargs)
12                except Exception as e:
13                    print(f"Tentative {attempt}/{max_attempts} échouée : {e}")
14                    if attempt < max_attempts:
15                        print(f"Nouvel essai dans {delay}s...")
16                        time.sleep(delay)
17                    else:
18                        print("Toutes les tentatives épuisées")
19                        raise
20        return wrapper
21    return decorator
22
23@retry(max_attempts=3, delay=0.5)
24def unstable_connection(success_rate=0.3):
25    """Simule une connexion instable"""
26    import random
27    if random.random() > success_rate:
28        raise ConnectionError("Connexion interrompue")
29    return "Succès !"
30
31# Réessaie automatiquement en cas d'erreur
32result = unstable_connection(success_rate=0.8)

Empiler plusieurs décorateurs

Les décorateurs peuvent être empilés - appliquer plusieurs à la fois !

1@decorator1
2@decorator2
3@decorator3
4def my_function():
5    pass

Cette écriture équivaut à un emballage manuel : my_function = decorator1(decorator2(decorator3(my_function))). decorator3 est le plus proche de la fonction, c'est donc lui qui l'enveloppe en premier.

L'ordre compte - exécuté de bas en haut !

1from functools import wraps
2
3def bold(func):
4    @wraps(func)
5    def wrapper(*args, **kwargs):
6        return f"**{func(*args, **kwargs)}**"
7    return wrapper
8
9def italic(func):
10    @wraps(func)
11    def wrapper(*args, **kwargs):
12        return f"_{func(*args, **kwargs)}_"
13    return wrapper
14
15@bold
16@italic
17def greet(name):
18    return f"Bonjour, {name}"
19
20print(greet("Darwin"))  # "**_Bonjour, Darwin_**"

D'abord italic a entouré le résultat de tirets bas, puis bold a ajouté les astérisques. L'ordre d'exécution est greet → italic → bold.

Décorateurs de classe

Les décorateurs peuvent aussi modifier des classes entières !

1def singleton(cls):
2    """Décorateur de classe - motif Singleton"""
3    instances = {}
4
5    @wraps(cls)
6    def get_instance(*args, **kwargs):
7        if cls not in instances:
8            instances[cls] = cls(*args, **kwargs)
9        return instances[cls]
10
11    return get_instance
12
13@singleton
14class DatabaseConnection:
15    def __init__(self, host):
16        self.host = host
17        print(f"Connexion à {host}...")
18
19# Première instance
20db1 = DatabaseConnection("localhost")  # Affiche : "Connexion à localhost..."
21
22# « Deuxième » instance - en réalité la même !
23db2 = DatabaseConnection("localhost")  # N'affiche rien
24
25print(db1 is db2)  # True - le même objet !

Exemple Safari - système complet avec décorateurs

Pour finir, une classe d'espèce où cinq décorateurs travaillent ensemble : journalisation et chronométrage des observations, validation de la population, autorisation pour la suppression et cache des calculs.

1from functools import wraps
2from datetime import datetime
3import time
4from typing import Callable, Any
5
6# === DÉCORATEURS ===
7
8def log_observation(func: Callable) -> Callable:
9    """Consigne toutes les observations d'espèces"""
10    @wraps(func)
11    def wrapper(*args, **kwargs):
12        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
13        print(f"[{timestamp}] Observation : {func.__name__}")
14        result = func(*args, **kwargs)
15        print(f"   Enregistré : {result}")
16        return result
17    return wrapper
18
19def measure_time(func: Callable) -> Callable:
20    """Mesure le temps d'exécution de l'opération"""
21    @wraps(func)
22    def wrapper(*args, **kwargs):
23        start = time.time()
24        result = func(*args, **kwargs)
25        elapsed = time.time() - start
26        print(f"   ⏱ Temps : {elapsed:.3f}s")
27        return result
28    return wrapper
29
30def validate_population(func: Callable) -> Callable:
31    """Valide les données de population avant l'enregistrement"""
32    @wraps(func)
33    def wrapper(self, population: int, *args, **kwargs):
34        if not isinstance(population, int):
35            raise TypeError("La population doit être un entier")
36        if population < 0:
37            raise ValueError("La population ne peut pas être négative")
38        return func(self, population, *args, **kwargs)
39    return wrapper
40
41def require_auth(authorization_level: str):
42    """Exige un niveau d'autorisation"""
43    def decorator(func: Callable) -> Callable:
44        @wraps(func)
45        def wrapper(self, auth_code: str, *args, **kwargs):
46            if auth_code != f"SAFARI_{authorization_level}":
47                raise PermissionError(
48                    f"Niveau requis : {authorization_level}"
49                )
50            return func(self, *args, **kwargs)
51        return wrapper
52    return decorator
53
54def cache_result(func: Callable) -> Callable:
55    """Met en cache les résultats de calcul"""
56    cache = {}
57
58    @wraps(func)
59    def wrapper(*args):
60        if args in cache:
61            print(f"   Cache hit")
62            return cache[args]
63
64        result = func(*args)
65        cache[args] = result
66        return result
67
68    return wrapper
69
70# === CLASSE AVEC DÉCORATEURS ===
71
72class Species:
73    """
74    Classe Species avec des décorateurs Safari
75
76    Démonstration : journalisation, chronométrage, validation, autorisation, cache
77    """
78
79    _registry = {}
80
81    def __init__(self, scientific_name: str, common_name: str, population: int):
82        self.scientific_name = scientific_name
83        self.common_name = common_name
84        self._population = population
85        self.observations = []
86
87        Species._registry[scientific_name] = self
88
89    @property
90    def population(self) -> int:
91        """Getter - property ordinaire"""
92        return self._population
93
94    @population.setter
95    @validate_population  # Décorateur de validation !
96    def population(self, value: int) -> None:
97        """Setter avec validation automatique"""
98        self._population = value
99
100    @log_observation  # Journalisation automatique
101    @measure_time     # Mesure du temps
102    def add_observation(self, location: str, count: int, date: str) -> str:
103        """
104        Ajoute une observation - automatiquement consignée et chronométrée
105
106        Décorateurs : @log_observation, @measure_time
107        """
108        time.sleep(0.1)  # Simule une opération
109        obs = {
110            "location": location,
111            "count": count,
112            "date": date
113        }
114        self.observations.append(obs)
115        return f"{count}x {self.common_name} à {location}"
116
117    @cache_result  # Met en cache les résultats
118    def calculate_biodiversity_score(self) -> float:
119        """
120        Calcule le score de biodiversité - avec cache
121
122        Opération chronophage - le cache fait gagner du temps !
123        """
124        print(f"   Calcul du score pour {self.common_name}...")
125        time.sleep(0.5)  # Simule des calculs complexes
126
127        total_obs = sum(obs["count"] for obs in self.observations)
128        unique_locations = len(set(obs["location"] for obs in self.observations))
129
130        return (total_obs * unique_locations) / (self.population + 1)
131
132    @require_auth("ADMIN")  # Nécessite une autorisation ADMIN
133    def delete_all_observations(self) -> str:
134        """
135        Supprime toutes les observations - nécessite une autorisation ADMIN
136
137        Décorateur : @require_auth("ADMIN")
138        """
139        count = len(self.observations)
140        self.observations.clear()
141        return f"Observations supprimées : {count}"
142
143    @classmethod
144    def get_total_population(cls) -> int:
145        """Population totale de toutes les espèces"""
146        return sum(s.population for s in cls._registry.values())
147
148    @staticmethod
149    @cache_result  # Même les méthodes statiques peuvent être mises en cache !
150    def calculate_extinction_risk(population: int, habitat_loss: float) -> str:
151        """
152        Calcule le risque d'extinction - avec cache
153
154        Args:
155            population: Nombre d'individus
156            habitat_loss: Perte d'habitat (0.0-1.0)
157        """
158        print(f"   Calcul du risque...")
159        time.sleep(0.3)
160
161        risk_score = (1000 - population) * habitat_loss
162
163        if risk_score > 800:
164            return "Critique"
165        elif risk_score > 500:
166            return "Élevé"
167        elif risk_score > 200:
168            return "Moyen"
169        else:
170            return "Faible"
171
172    def __repr__(self) -> str:
173        return f"Species('{self.common_name}', pop={self.population})"
174
175# === DÉMONSTRATION ===
176
177print("=== SYSTÈME SAFARI AVEC DÉCORATEURS ===\n")
178
179# Création des espèces
180lion = Species("Panthera leo", "Lion", 120)
181rhino = Species("Diceros bicornis", "Rhinocéros", 45)
182
183# 1. Observations - journalisation et chronométrage automatiques
184print("\n1. OBSERVATIONS (avec @log_observation et @measure_time) :")
185lion.add_observation("Serengeti", 12, "2024-01-15")
186lion.add_observation("Masai Mara", 8, "2024-01-16")
187
188# 2. Validation de la population
189print("\n2. VALIDATION (@validate_population) :")
190try:
191    lion.population = -10  # Erreur - négatif !
192except ValueError as e:
193    print(f"Erreur de validation : {e}")
194
195try:
196    lion.population = "cent"  # Erreur - pas un int !
197except TypeError as e:
198    print(f"Erreur de type : {e}")
199
200lion.population = 125  # OK
201print(f"Population mise à jour : {lion.population}")
202
203# 3. Cache - score de biodiversité
204print("\n3. CACHE (@cache_result) :")
205print("Premier appel :")
206score1 = lion.calculate_biodiversity_score()
207print(f"Score : {score1:.2f}")
208
209print("\nDeuxième appel (depuis le cache) :")
210score2 = lion.calculate_biodiversity_score()
211print(f"Score : {score2:.2f}")
212
213# 4. Autorisation
214print("\n4. AUTORISATION (@require_auth) :")
215try:
216    lion.delete_all_observations("WRONG_CODE")
217except PermissionError as e:
218    print(f"Accès refusé : {e}")
219
220result = lion.delete_all_observations("SAFARI_ADMIN")
221print(f"{result}")
222
223# 5. Cache dans une méthode statique
224print("\n5. CACHE DANS UNE MÉTHODE STATIQUE :")
225print("Premier appel :")
226risk1 = Species.calculate_extinction_risk(50, 0.8)
227print(f"Risque : {risk1}")
228
229print("\nDeuxième appel (depuis le cache) :")
230risk2 = Species.calculate_extinction_risk(50, 0.8)
231print(f"Risque : {risk2}")
232
233print("\nParamètres différents (nouveau calcul) :")
234risk3 = Species.calculate_extinction_risk(500, 0.3)
235print(f"Risque : {risk3}")

Résumé

Dans cette leçon, tu as appris :

  • Ce que sont les décorateurs et comment ils fonctionnent
  • La syntaxe @decorator
  • Créer des décorateurs avec *args et **kwargs
  • Utiliser @functools.wraps pour préserver les métadonnées
  • Les décorateurs avec paramètres (trois niveaux de fonctions)
  • Les décorateurs intégrés (@property, @classmethod, @staticmethod)
  • Des exemples concrets : chronométrage, journalisation, cache, autorisation, réessai
  • Empiler plusieurs décorateurs
  • Les décorateurs de classe
  • Un système Safari complet avec décorateurs

Point de contrôle

Avant de passer à la suite :

  • Tu comprends comment les décorateurs modifient les fonctions
  • Tu sais créer un décorateur simple
  • Tu sais quand utiliser @wraps
  • Tu comprends les décorateurs avec paramètres
  • Tu sais empiler plusieurs décorateurs

Analogie Safari : les décorateurs sont des adaptations évolutives - un caméléon avec des chromatophores, une chauve-souris avec l'écholocation - ils étendent les capacités sans changer la nature fondamentale !

Félicitations ! Tu as terminé le module 3 - Programmation orientée objet ! Tu maîtrises les classes, l'héritage, l'encapsulation, les méthodes magiques, les type hints et les décorateurs. Tu es prêt pour les prochains défis de Python Safari !

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. En Python, un décorateur est :

  2. 2. Par convention, comment s'appelle le premier paramètre d'une méthode marquée avec @classmethod ?

Voici 2 des 6 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Tri horizontal

    Range les éléments dans le bon ordre :

  • Clique dans l'ordre

    Clique sur les éléments dans le bon ordre :

  • Éditeur de code

    Crée une @classmethod from_dict(cls, data) qui crée un Animal à partir d'un dictionnaire avec les clés 'name' et 'species', par ex. Animal.from_dict({'name': 'Simba', 'species': 'Lion'})

  • Éditeur de code

    Crée une @staticmethod is_dangerous(species) qui renvoie True pour les espèces 'Lion', 'Crocodile' et 'Hippopotame', et False pour les autres

  • Tri horizontal

    Range l'en-tête de la méthode de classe from_dict (la ligne sous le décorateur @classmethod) :

  • Tri vertical

    Range ce qui se passe après l'appel de print(lion) pour un objet doté d'une méthode __str__ :

  • Clique dans l'ordre

    Clique sur les éléments dans le bon ordre :

  • Éditeur de code

    Crée les classes : Animal (base), Mammal(Animal), Bird(Animal), Tiger(Mammal), Eagle(Bird)

  • Éditeur de code

    Dans la classe Animal(name, age, health_status), stocke age et health_status dans des attributs protégés ou privés (self._age ou self.__age) et ajoute les properties age et health_status

  • Tri vertical

    Range les étapes de création d'une classe enfant :

  • Éditeur de code

    Crée une classe Zoo avec les méthodes add_animal(animal), remove_animal(name) (supprime l'animal portant ce nom), find_by_species(species) et list_all() (les deux renvoient des listes d'animaux). Les animaux sont des objets Animal(name, species)

  • Éditeur de code

    Assemble Animal et Zoo dans un programme : crée zoo = Zoo() et, dans une boucle while, lis le choix avec input() : 1 - ajouter (nom, espèce), 2 - supprimer (nom), 3 - chercher par espèce et afficher les noms, 0 - quitter

  • Tri vertical

    Range les types de méthodes selon leur premier paramètre : d'abord ceux qui reçoivent l'objet (self), puis la classe (cls), et enfin ceux sans self ni cls :

Articles utiles