Classes et objets en Python : programmation orientée objet

Apprends à écrire tes propres classes en Python. Définis une classe avec un constructeur et des attributs, crée des objets à partir de cette classe et donne-leur des méthodes qui utilisent et modifient leurs données sans compromettre leur validité. Utilise des méthodes spéciales pour afficher clairement les objets, les comparer avec ==, les trier et les combiner avec +. Construis des programmes dans lesquels des objets contiennent d’autres objets et appellent leurs méthodes, puis détermine quand une classe rend un programme plus clair que des fonctions et des dictionnaires.

DébutantUnité 10 sur 10 de la formation Formation : Python : variables, boucles, fonctions et fichiers

Ce que tu vas apprendre

  1. Découvre que chaque valeur Python est un objet qui possède ses propres données et méthodes, puis écris ta propre classe et crée des objets à partir de celle-ci.
    • Reconnaître les objets que tu utilises déjà
      Décris les chaînes de caractères, les listes et les dictionnaires comme des objets qui ont un type, contiennent des données et proposent des méthodes que tu appelles avec la notation pointée.
    • Définir une classe et créer des instances
      Écris une instruction de classe, crée plusieurs objets distincts à partir de celle-ci, puis définis et lis leurs attributs avec la notation pointée.
  2. Utilise un constructeur pour donner à chaque nouvel objet les attributs dont il a besoin, choisis entre attributs d’instance et attributs de classe, et empêche la création d’objets invalides.
    • Écrire un constructeur avec __init__ et self
      Définis __init__ pour que chaque nouvel objet commence avec ses propres attributs, tirés des arguments ou fixés à des valeurs initiales.
    • Choisir entre attributs d’instance et attributs de classe
      Détermine si une valeur appartient à chaque objet ou à la classe dans son ensemble, et prévois comment Python trouve et modifie chaque type d’attribut.
    • Garantir la validité des objets dès leur création
      Vérifie les arguments reçus par un constructeur, lève une exception claire pour les valeurs invalides et respecte les règles qui donnent un sens à un objet.
  3. Écris des méthodes qui utilisent et modifient les données d’un objet, et protège ces données contre les modifications qui rendraient l’objet invalide.
    • Définir et appeler des méthodes d’instance
      Écris des méthodes qui utilisent self, appelle-les sur des objets et renvoie des résultats au lieu de les afficher.
    • Modifier l’état d’un objet avec des méthodes
      Écris des méthodes qui mettent à jour les attributs tout en préservant la validité de l’objet, et prévois quand une modification est visible par d’autres noms désignant le même objet.
    • Protéger les attributs avec des conventions de nommage et des propriétés
      Signale les attributs internes avec un tiret bas et utilise des propriétés pour vérifier les nouvelles valeurs ou calculer des valeurs qui restent à jour.
  4. Écris des méthodes spéciales qui permettent à tes objets de présenter un texte lisible, d’être comparés avec == et <, et de fonctionner avec des opérateurs et des fonctions natives.
    • Donner aux objets des représentations textuelles lisibles
      Écris __str__ et __repr__ pour que les objets présentent un texte utile lorsqu’ils sont affichés, examinés ou stockés dans une liste.
    • Comparer et combiner des objets avec des opérateurs
      Définis __eq__, __lt__, __add__ et __len__ pour que tes objets fonctionnent avec ==, le tri, + et len(), et détermine quand un opérateur est plus clair qu’une méthode nommée.
  5. Combine des objets pour que chacun gère ses propres données, suis l’exécution de programmes dans lesquels des objets s’appellent et détermine quand une classe rend un programme plus clair.
    • Construire des objets qui contiennent d’autres objets
      Stocke des objets comme attributs ou dans des listes à l’intérieur d’un autre objet, et écris des méthodes qui agissent sur toute la collection.
    • Faire interagir des objets au moyen de méthodes
      Passe des objets à des méthodes et à des fonctions, renvoie de nouveaux objets et suis l’exécution d’un court programme dans lequel plusieurs objets coopèrent.
    • Décider quand une classe rend un programme plus clair
      Détermine quand il est utile de réunir données et comportement dans une classe, conçois une petite classe à partir d’une description et réécris du code fondé sur un dictionnaire sous forme de classe.

Classes et objets en Python : cours

5 chapitres

1

Comprendre les objets et définir ta première classe

Les objets sont déjà présents dans tous les programmes Python que tu écris : une chaîne de caractères possède des méthodes, une liste modifie son contenu et un dictionnaire permet des accès par clé. Voir ces opérations familières comme des comportements d’objets te donne un point de départ pour créer tes propres classes. Découvre comment les types, l’identité et les références partagées influencent un programme, puis définis une petite classe et donne à des instances distinctes leurs propres attributs.

1.1 Reconnaître les objets que tu utilises déjà

Tu as déjà utilisé des objets chaque fois que tu as manipulé un nombre, une chaîne de caractères, une liste ou un dictionnaire. Un objet a un type, une valeur ou un état, et des opérations qu’il permet. Même les fonctions et None sont des objets. Écrire ta propre classe étend ce système familier.

Trouver le type et les méthodes d’un objet

type("hello") renvoie <class 'str'> ; type([1, 2]) renvoie <class 'list'>. Les mots type et classe décrivent ici le même rôle. isinstance(3.5, float) vaut True, tandis que isinstance("3.5", float) vaut False : la seconde valeur est du texte, même si elle ressemble à un nombre.

Une méthode est une fonction fournie par un type. Dans "ada lovelace".title(), la chaîne avant le point est l’objet, title est la méthode et les parenthèses l’appellent. Le résultat est "Ada Lovelace". dir(value) liste les noms proposés par un objet ; des noms comme __len__ sont des méthodes spéciales qui relient les objets à des opérations Python comme len(value).

Suivre le résultat et l’objet d’origine

Un appel de méthode peut renvoyer un résultat, modifier un objet ou faire les deux. Vérifie le comportement de la méthode avant de décider quoi affecter.

word = "level"
print(word.count("l"))  # 2
print(word.upper())     # LEVEL
print(word)             # level

nums = [3, 1, 2]
result = nums.sort()
print(nums)             # [1, 2, 3]
print(result)           # None

Les chaînes de caractères sont immuables : leur contenu ne peut pas changer. upper() renvoie une autre chaîne, donc word garde sa valeur d’origine. Pour conserver le résultat en majuscules sous ce nom, écris word = word.upper().

Les listes sont mutables. sort() modifie la liste en place et renvoie None. Écrire nums = nums.sort() fait perdre la référence à la liste contenue dans nums et affecte None. Utilise nums.sort() sur une ligne à part, ou nums = sorted(nums) pour obtenir une nouvelle liste triée. Pour un dictionnaire, {"a": 1}.get("b", 0) renvoie 0 parce que la clé demandée est absente.

Les parenthèses comptent aussi. word.upper est un objet méthode ; word.upper() l’exécute. De nombreux objets peuvent proposer le même comportement : "cat".upper() et "dog".upper() utilisent un même type de méthode avec des chaînes différentes.

Distinguer égalité et identité

L’égalité indique si les valeurs correspondent. L’identité indique si deux noms désignent un même objet.

a = [1, 2]
b = [1, 2]
c = a
print(a == b)  # True
print(a is b)  # False
print(c is a)  # True
c.append(3)
print(a)       # [1, 2, 3]
print(b)       # [1, 2]

a et b ont au départ des contenus égaux, mais sont des listes distinctes. c = a crée un alias, un autre nom pour la même liste. L’ajout par l’intermédiaire de c modifie l’objet que a désigne aussi. Il ne modifie pas la liste distincte contenue dans b. Pour suivre un programme, dessine une boîte par objet et une flèche de chaque nom vers sa boîte.

Points clés

  • Chaque valeur Python est un objet avec un type, un état et des opérations qu’il permet.
  • Appelle une méthode avec des parenthèses ; une référence à la méthode seule ne l’exécute pas.
  • Vérifie si une méthode renvoie une nouvelle valeur ou modifie l’objet existant.
  • == compare les valeurs ; is compare l’identité.
  • Une affectation peut donner à deux noms des références au même objet mutable.

1.2 Définir une classe et créer des instances

Une classe définit un type que tu peux utiliser dans ton propre programme. Chaque objet créé à partir de cette classe est une instance. Une classe décrit le type de chose ; les instances contiennent les données de choses particulières. Un formulaire vierge et ses copies remplies offrent une comparaison utile.

Écrire une classe et l’appeler

Une instruction de classe comprend le mot-clé class, un nom, deux-points et un corps indenté. pass fournit un corps vide pendant que tu essaies la structure.

class Book:
    pass

b1 = Book()
b2 = Book()
print(b1 is b2)           # False
print(isinstance(b1, Book))  # True

Chaque appel à Book() crée une instance distincte. Dans un script, type(b1) affiche <class '__main__.Book'>. L’instruction de classe doit être exécutée avant que le code essaie d’utiliser Book. Tu peux aussi définir une classe dans un module et l’importer avec from library import Book, puis créer des instances dans le programme appelant.

Suis les conventions de nommage de Python : Book, BankAccount et ShoppingCart utilisent CapWords, chaque mot commençant par une majuscule. Les variables et les méthodes utilisent des mots en minuscules séparés par des tirets bas, comme book_count et add_item. Utilise quatre espaces pour chaque niveau d’indentation.

Stocker des données sur chaque instance

Un attribut est une valeur nommée à laquelle on accède avec un point. Affecter une valeur à un attribut la stocke ; lire le même nom permet de la retrouver.

b1.title = "Dune"
b2.title = "Emma"
b1.pages = 412
print(b1.title)  # Dune
print(b2.title)  # Emma
print(b1.pages)  # 412

Les données appartiennent à l’instance choisie. Donner un attribut pages à b1 n’en donne pas un à b2. Lire b2.pages lève maintenant AttributeError, car cet objet n’a pas d’attribut nommé pages.

Les affectations peuvent créer de nouveaux noms sans vérifier ton intention. Après b1.titel = "X", le b1.title d’origine contient toujours "Dune" ; l’objet a gagné un second attribut dont le nom est mal orthographié. Si une valeur semble ne pas changer, compare l’orthographe à l’endroit où elle a été affectée et à celui où elle a été lue.

Distinguer la classe de ses instances
ExpressionSignification
BookL’objet classe, qui définit le type
Book()Un appel qui crée une instance
b1.titleLe titre stocké sur ce livre
Book.titleUn attribut demandé sur la classe elle-même

b = Book donne un autre nom à la classe. Cela ne crée pas de livre. Si les titres ont été définis uniquement sur les instances, demander Book.title lève AttributeError ; aucun titre unique n’est partagé par le type.

Donner à chaque objet un départ fiable

Définir les attributs depuis l’extérieur est possible, mais cela disperse l’initialisation dans le programme. Un livre pourrait recevoir un titre et un nombre de pages tandis qu’un autre ne recevrait qu’un titre. Le code ultérieur ne peut pas supposer sans risque que les deux ont pages.

Une méthode d’initialisation appelée __init__ place les affectations initiales dans la classe. Chaque appel de création suit alors le même processus d’initialisation. La classe peut imposer un titre et créer d’autres attributs avec des valeurs initiales adaptées, afin que le code appelant n’ait pas à retenir une séquence d’affectations distincte.

Points clés

  • Une classe définit un type ; une instance est un objet créé à partir de cette classe.
  • Utilise ClassName() pour créer une instance et la notation pointée pour accéder à ses attributs.
  • Chaque instance peut contenir des données différentes sous les mêmes noms d’attributs.
  • Un attribut absent lève AttributeError ; une affectation à un nom mal orthographié peut créer un nouvel attribut.
2

Initialiser des objets avec des constructeurs et des attributs

Un objet doit avoir un état initial fiable avant que ses méthodes puissent effectuer un travail utile. L’initialisation relie les arguments d’un appel de classe aux attributs stockés sur la nouvelle instance. Choisis les valeurs qui appartiennent à un objet et celles qui appartiennent à la classe, puis valide les entrées avant de les accepter. Suivre la recherche d’attributs et les échecs d’initialisation t’aidera à repérer les partages involontaires et les données manquantes.

2.1 Écrire un constructeur avec __init__ et self

Chaque compte nouvellement créé a besoin d’un titulaire et d’un solde initial. Placer cette initialisation dans __init__ donne à chaque objet les attributs attendus par les méthodes ultérieures. Python crée d’abord l’instance, puis appelle __init__ pour l’initialiser.

Relier les arguments aux attributs
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
        self.transactions = []

acct = BankAccount("Maya", 50)
other = BankAccount("Leo")

Pour le premier appel, Python passe le nouveau compte comme self, "Maya" comme owner et 50 comme balance. Les affectations donnent à acct.owner la valeur "Maya" et à acct.balance la valeur 50. Le second appel utilise le solde par défaut, donc other.balance vaut 0.

self est le paramètre qui reçoit l’objet courant. C’est une convention, pas un mot-clé Python. Utilise ce nom pour que les lecteurs reconnaissent immédiatement l’objet. Les appelants l’omettent : BankAccount("Maya", 50) fournit deux arguments, pas trois.

Distinguer les noms locaux des données stockées

Dans self.owner = owner, le nom à droite est un paramètre local. Le nom à gauche est un attribut stocké sur l’objet. Une fois l’initialisation terminée, les autres méthodes peuvent toujours utiliser self.owner.

Écrire balance = balance ne fait que réaffecter le nom local. Cela ne crée aucun attribut, donc la lecture ultérieure de acct.balance échoue. Place self. devant chaque attribut qui doit rester associé à l’objet.

Prépare l’initialisation en listant ce dont chaque instance a besoin. Les valeurs obligatoires deviennent des paramètres après self ; les valeurs facultatives peuvent avoir des valeurs par défaut. Les valeurs qui commencent toujours de la même manière peuvent être affectées dans le corps, comme self.visits = 0. Une liste créée par self.transactions = [] est créée à nouveau à chaque appel.

Éviter une liste par défaut partagée

Un argument par défaut est évalué lorsque Python définit la méthode. Avec def __init__(self, items=[]), les appels qui omettent items reçoivent la même liste. Si l’initialiseur stocke directement cette liste, modifier les éléments d’un objet change ceux que voit un autre objet.

Utilise None comme valeur par défaut et crée une nouvelle liste à chaque appel :

class Cart:
    def __init__(self, items=None):
        self.items = [] if items is None else list(items)

a = Cart()
b = Cart()
a.items.append("pen")
print(b.items)  # []

list(items) crée aussi une nouvelle liste externe lorsqu’un appelant en fournit une. Les objets à l’intérieur de cette liste restent des références partagées ; créer la liste ne recrée pas chaque élément.

Lire les erreurs d’initialisation

Il manque à BankAccount() l’argument obligatoire owner, ce qui lève TypeError. Compare l’appel aux paramètres après self.

Le nom __init__ comporte deux tirets bas de chaque côté. _init_ est un nom de méthode ordinaire et n’effectuera pas cette initialisation automatiquement. __init__ doit renvoyer None : omets donc normalement l’instruction de retour. Renvoyer un solde lève TypeError ; l’appel de classe lui-même fournit la nouvelle instance à l’appelant.

Points clés

  • __init__ initialise l’instance après sa création par Python.
  • Python fournit self ; les appelants fournissent les autres arguments.
  • self.name = name stocke un paramètre comme attribut d’instance.
  • Crée les listes propres à chaque instance pendant l’initialisation ; utilise None au lieu d’une valeur par défaut mutable.
  • __init__ doit renvoyer None.

2.2 Choisir entre attributs d’instance et attributs de classe

Certaines valeurs décrivent un objet ; d’autres décrivent le type dans son ensemble. La taille d’une pizza appartient à cette pizza. Une limite de garnitures ou un compte des pizzas créées peut appartenir à la classe. L’endroit où se trouve une affectation détermine le type d’attribut qu’elle crée.

Placer la valeur au bon endroit
class Pizza:
    MAX_TOPPINGS = 5
    count = 0

    def __init__(self, size):
        self.size = size
        self.toppings = []
        Pizza.count += 1

p1 = Pizza("large")
p2 = Pizza("small")

size et toppings sont des attributs d’instance, définis sur self. Chaque pizza a ses propres valeurs. MAX_TOPPINGS et count sont des attributs de classe, affectés dans le corps de la classe en dehors d’une méthode. Les majuscules indiquent une valeur destinée à être constante ; elles n’empêchent pas sa réaffectation.

Après ces appels, Pizza.count vaut 2. Chaque initialiseur met à jour le même attribut de classe. p1.size vaut "large", tandis que p2.size vaut "small".

Suivre la recherche et le masquage

Pour des attributs de données ordinaires comme ceux-ci, la lecture de p1.MAX_TOPPINGS vérifie d’abord l’instance, puis la classe. Puisqu’aucune valeur n’est stockée sur p1, Python trouve Pizza.MAX_TOPPINGS, qui vaut 5.

Une affectation par l’intermédiaire d’une instance a un autre effet :

p1.MAX_TOPPINGS = 8
Pizza.MAX_TOPPINGS = 6
print(p1.MAX_TOPPINGS)     # 8
print(p2.MAX_TOPPINGS)     # 6
print(Pizza.MAX_TOPPINGS)  # 6

La première ligne crée un attribut d’instance sur p1. Il masque la valeur de classe pour cet objet. Modifier ensuite la valeur de classe atteint p2, qui lit toujours la valeur de la classe, mais ne remplace pas la valeur propre de p1.

AffectationCe qui change
self.size = sizeLes données sur l’instance courante
Pizza.count += 1Le compteur de classe partagé
p1.MAX_TOPPINGS = 8Une valeur d’instance qui masque la valeur de classe
Repérer deux formes de partage involontaire

Si l’initialiseur utilisait self.count += 1, Python lirait le 0 de la classe, ajouterait un et stockerait un attribut d’instance. Chaque nouvelle pizza recevrait son propre count de 1, tandis que Pizza.count resterait à 0. Nomme la classe lorsque tu mets à jour un compteur commun à toute la classe.

Une liste dans le corps de la classe provoque un autre problème. Avec toppings = [], les instances qui n’ont pas d’attribut de garnitures distinct trouvent toutes la même liste. p1.toppings.append("olives") modifie cette liste partagée ; cette instruction n’affecte pas de nouvel attribut d’instance. Place self.toppings = [] dans __init__ lorsque chaque pizza a besoin de ses propres garnitures.

Examiner les données propres d’un objet

Pour le p2 d’origine, vars(p2) donne {'size': 'small', 'toppings': []}. Les valeurs de classe sont absentes parce qu’elles se trouvent sur Pizza. C’est utile pour déboguer une recherche dont le résultat surprend : vérifie si l’instance a sa propre valeur, puis examine la valeur de classe. Un paramètre partagé appartient à la classe ; un nom, une taille, un solde ou une collection personnelle appartient à l’instance.

Points clés

  • Les attributs d’instance contiennent les données d’un objet ; les attributs de classe contiennent des paramètres partagés ou des données concernant toute la classe.
  • Un attribut de données d’instance peut masquer un attribut de classe de même nom.
  • Mets à jour un compteur partagé par l’intermédiaire du nom de la classe.
  • Modifier une liste au niveau de la classe change l’unique liste partagée.
  • vars(obj) montre les attributs stockés sur l’objet lui-même, pas les attributs de classe qu’il peut trouver.

2.3 Garantir la validité des objets dès leur création

Un objet doit commencer dans un état que ses méthodes peuvent utiliser. Un rectangle de largeur négative ne peut pas représenter la forme voulue. Une règle qui doit toujours rester vraie pour chaque objet d’une classe est un invariant. La validation vérifie ces règles avant d’accepter les données.

Vérifier avant de stocker
class Rectangle:
    def __init__(self, width, height):
        if width <= 0:
            raise ValueError("width must be positive")
        if height <= 0:
            raise ValueError("height must be positive")
        self.width = width
        self.height = height

Rectangle(3, 4) passe les deux vérifications et renvoie un objet initialisé. Rectangle(-2, 4) lève ValueError à la première vérification. Les affectations suivantes ne s’exécutent jamais. Si le programme n’intercepte pas cette exception, l’exécution s’arrête à cet endroit.

Supposons que r désigne déjà un rectangle valide. Dans r = Rectangle(-2, 4), l’appel à droite échoue avant l’affectation. r garde sa valeur précédente. Si r n’avait pas de valeur avant l’échec de l’appel, l’affectation ne lui en donne pas.

Choisir une exception utile
ExceptionProblème décrit
ValueErrorUne entrée de type acceptable a une valeur inacceptable
TypeErrorL’entrée n’a pas le bon type pour l’opération

Une largeur numérique négative est une erreur de valeur. Du texte fourni là où un nombre est nécessaire est une erreur de type. Dans cet exemple, la comparaison avec 0 lève déjà TypeError pour du texte. Une classe peut aussi vérifier explicitement un type avec isinstance lorsqu’elle a besoin d’un message plus clair.

Écris des messages qui nomment la règle, comme "width must be positive". Un message qui dit seulement "bad input" laisse l’appelant deviner quelle valeur doit être corrigée.

Laisser le code appelant gérer la reprise
while True:
    try:
        width = float(input("Width: "))
        room = Rectangle(width, 3)
        break
    except ValueError as error:
        print("Try again:", error)

La saisie de abc échoue dans float ; celle de -2 passe la conversion mais échoue dans Rectangle. Les deux lèvent ValueError, donc cette boucle gère les deux cas. La classe énonce sa règle sur les données, tandis que le code appelant décide de demander une nouvelle saisie.

Afficher un avertissement dans __init__ ne rejette pas l’appel. Si l’exécution continue, le programme peut recevoir un objet invalide et échouer plus tard en l’utilisant. Lever une exception arrête l’initialisation qui échoue à l’endroit où le problème se produit.

Nettoyer les données délibérément et maintenir la règle

Un nettoyage documenté peut être utile. self.name = name.strip() supprime les espaces aux extrémités ; valide le nom nettoyé si les noms vides sont interdits. self.items = list(items) crée une liste externe distincte : ajouter un élément à la liste de l’appelant ne l’ajoute donc pas à celle-ci. Remplacer une largeur invalide par sa valeur absolue changerait le sens voulu par l’appelant ; cela nécessite donc une règle de conception explicite.

Les vérifications de l’initialisation s’exécutent à la création. Une simple affectation comme r.width = -5 les contourne toujours par la suite. Les méthodes qui modifient une largeur doivent la vérifier à nouveau. Une propriété peut placer une vérification sur l’affectation à l’attribut elle-même, ce qui permet d’appliquer la même règle pendant l’initialisation et les mises à jour ultérieures.

Points clés

  • Un invariant est une règle que l’état valide d’un objet doit respecter.
  • Valide les données avant de les accepter et lève une exception claire en cas d’échec.
  • Un appel de création qui échoue n’affecte pas de nouvel objet au nom de l’appelant.
  • Les vérifications du constructeur doivent aussi être assurées par les méthodes ou propriétés ultérieures qui modifient l’état.
3

Donner un comportement aux objets avec des méthodes d’instance

Les méthodes transforment les données stockées en comportements utiles. Un rectangle peut calculer son aire et un compte peut mettre à jour son solde tout en enregistrant une transaction. Suis la manière dont Python passe l’objet récepteur à chaque méthode, puis distingue un résultat renvoyé d’un changement d’état. Les propriétés permettent à une classe de conserver un accès lisible aux attributs tout en vérifiant les affectations ou en calculant les valeurs à la demande.

3.1 Définir et appeler des méthodes d’instance

Une méthode d’instance est une fonction définie dans une classe qui utilise l’objet passé comme self. L’objet fournit ses données stockées ; des paramètres supplémentaires fournissent les autres informations nécessaires à l’action.

Lire les attributs par self

Ajoute ces méthodes dans la classe Rectangle avec validation, au même niveau d’indentation que __init__ :

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

    def is_square(self):
        return self.width == self.height

Avec r = Rectangle(3, 4), r.area() renvoie 12, r.perimeter() renvoie 14 et r.is_square() renvoie False. Chaque méthode lit les attributs du même objet. Un second rectangle utilise les mêmes définitions de méthodes avec sa propre largeur et sa propre hauteur.

Dans une méthode, self.width désigne un attribut. Un simple width désigne une variable locale ou une autre variable accessible ; Python ne la cherche pas automatiquement sur l’objet courant. Si aucune variable de ce nom n’existe, return width * height lève NameError. Écrire self. indique explicitement à quel objet appartiennent les données voulues.

Suivre l’appel

Pour cette classe, r.area() fonctionne comme Rectangle.area(r). Python trouve la fonction sur la classe et fournit r comme premier argument. Une référence à une méthode comme r.area est une méthode liée : elle mémorise la fonction et ce rectangle. Ajouter () effectue l’appel.

Si une méthode est définie par def resize(self, factor), appelle-la sous la forme r.resize(2). r remplit self et 2 remplit factor. Tu ne passes pas r une seconde fois. Si une méthode d’instance est écrite par erreur sous la forme def area():, un appel sur r lève TypeError, car Python fournit quand même l’objet à une fonction qui n’accepte aucun argument.

Une méthode peut en utiliser une autre par l’intermédiaire de self :

    def double_area(self):
        return self.area() * 2

Ici, self.area() effectue le calcul avec le même rectangle. Omettre self. désignerait une fonction distincte appelée area, pas la méthode de l’objet.

Renvoyer des résultats à l’appelant

Une méthode de calcul doit normalement renvoyer son résultat. Le renvoi donne au code appelant une valeur à stocker, combiner, tester ou afficher :

r1 = Rectangle(3, 4)
r2 = Rectangle(2, 5)
total = r1.area() + r2.area()
print(total)  # 22

Si area se contentait d’afficher sa réponse, chaque appel renverrait None. L’addition échouerait alors avec TypeError, même si deux nombres étaient apparus à l’écran. Afficher est une action ; renvoyer fournit une valeur à l’expression qui contient l’appel.

Utilise des noms de méthodes qui montrent leur rôle. deposit et resize désignent des actions ; is_square et has_room suggèrent des vérifications booléennes. Une méthode n’a pas besoin de paramètres pour les données déjà stockées sur son objet. Elle a besoin de paramètres pour les nouvelles entrées, comme un facteur de redimensionnement ou un montant à déposer.

Points clés

  • Place self en premier dans une méthode d’instance ; Python fournit l’objet récepteur.
  • Lis les attributs et appelle les autres méthodes par l’intermédiaire de self..
  • Les parenthèses exécutent une méthode liée.
  • Renvoie les valeurs calculées pour que les appelants puissent les combiner, les tester ou les afficher.

3.2 Modifier l’état d’un objet avec des méthodes

L’état d’un objet est l’ensemble des valeurs actuelles de ses attributs. Un accesseur permet de consulter cet état. Un mutateur le modifie. Un mutateur utile vérifie ses entrées et tient à jour tous les attributs concernés, afin que le code appelant n’ait pas à coordonner plusieurs mises à jour.

Valider d’abord l’ensemble de la modification

Ajoute ces méthodes dans la classe BankAccount avec son titulaire, son solde et sa liste de transactions :

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("deposit must be positive")
        self.balance += amount
        self.transactions.append(("deposit", amount))

    def withdraw(self, amount):
        if amount <= 0:
            raise ValueError("withdrawal must be positive")
        if amount > self.balance:
            raise ValueError("insufficient funds")
        self.balance -= amount
        self.transactions.append(("withdraw", amount))

Un dépôt modifie le solde et enregistre l’opération. Un retrait vérifie à la fois le signe du montant et le solde disponible avant de soustraire. Ces vérifications empêchent un retrait refusé de modifier le solde ou son historique.

En partant de BankAccount("Maya", 50), un dépôt de 25 donne 75, et un retrait de 30 donne 45. Une tentative ultérieure de retrait de 100 lève ValueError ; elle laisse le solde à 45 et n’ajoute aucune transaction. Placer la soustraction avant la vérification des fonds modifierait l’état même en cas d’échec.

Distinguer une modification d’une valeur renvoyée

Ces mutateurs n’ont pas d’instruction de retour, donc ils renvoient None. Après new_balance = acct.deposit(25), le dépôt a eu lieu, mais new_balance vaut None. Lis ensuite acct.balance pour obtenir la valeur mise à jour.

Une classe peut proposer un autre type de méthode qui renvoie un nouvel objet. resize suggère de modifier un rectangle ; resized peut être choisi pour une méthode qui renvoie un nouveau rectangle et laisse l’original intact. Lis le contrat de la méthode : le nom est utile, mais c’est sa définition qui détermine le comportement.

Suivre les références partagées
a = BankAccount("Maya", 50)
b = a
b.deposit(10)
print(a.balance)  # 60
print(a is b)     # True

Une affectation lie un nom à un objet ; elle ne copie pas cet objet. Les deux noms donnent accès au compte dont le solde a changé. Passer a à une fonction donne aussi accès à cet objet. Une fonction qui appelle account.withdraw(5) peut donc modifier le compte vu par son appelant.

Pour obtenir un compte indépendant avec le même titulaire et le même solde, crée BankAccount(a.owner, a.balance). Ce constructeur fournit aussi un nouvel historique vide. C’est un nouveau compte, pas une copie complète de l’historique.

copy.copy(a) crée un nouvel objet externe mais conserve des références aux mêmes objets internes, notamment la liste des transactions. copy.deepcopy(a) copie aussi les objets imbriqués. Lorsque l’indépendance compte, vérifie les objets contenus dans les attributs ainsi que l’instance externe.

Affecter directement acct.balance = -500 contourne les vérifications des deux méthodes. Les conventions pour les attributs internes et les propriétés aident à rendre claire la façon prévue de faire les mises à jour et à garder la validation près des changements d’état.

Points clés

  • Vérifie une modification demandée avant de mettre à jour les attributs.
  • Un mutateur peut modifier l’état tout en renvoyant None.
  • L’affectation et le passage d’arguments conservent des références au même objet.
  • Une copie superficielle peut toujours partager des objets mutables imbriqués.
  • Garde les mises à jour d’état liées dans la méthode qui en est responsable.

3.3 Protéger les attributs avec des conventions de nommage et des propriétés

Le code appelant a besoin d’un moyen clair de lire et de modifier un objet. L’encapsulation conserve les règles relatives aux données avec les méthodes qui les gèrent. Python utilise des conventions et des propriétés pour exprimer cette interface.

Signaler le stockage interne

Un tiret bas initial dans _balance signale un attribut interne. Les appelants doivent utiliser les méthodes du compte, mais Python autorise toujours l’accès direct. Il n’existe pas de mot-clé private qui verrouille l’accès à la valeur.

Un nom comme __balance, avec deux tirets bas au début et sans double tiret bas à la fin, déclenche la déformation des noms. Dans class Acc, self.__balance est stocké sous _Acc__balance. À l’extérieur, obj.__balance échoue, mais obj._Acc__balance peut toujours fonctionner. La déformation évite les conflits de noms ; elle ne protège pas les données sensibles.

Exécuter une méthode avec la syntaxe d’un attribut

Une propriété fait exécuter une méthode lorsqu’un attribut est lu. La ligne @property est un décorateur : elle change la manière dont Python expose la méthode. Une propriété sans setter permet la lecture mais refuse l’affectation.

class Account:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance

    @property
    def balance(self):
        return self._balance

Après acct = Account("Maya", 50), lis acct.balance sans parenthèses. L’affectation acct.balance = 10 lève AttributeError, car cette propriété n’a pas de setter. Une méthode de dépôt dans la classe pourrait mettre à jour _balance après ses vérifications.

Vérifier une affectation avec un setter
class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not 0 <= value <= 100:
            raise ValueError("score must be between 0 and 100")
        self._score = value

L’accesseur et le setter portent tous deux le nom score ; la valeur stockée utilise _score. À la création, self.score = score exécute le setter.

Pour s = Student("Ana", 88), l’affectation s.score = 92 stocke 92. L’affectation s.score = -1 lève ValueError avant le stockage, donc le score reste à 92. Student("Ana", 105) échoue pendant l’initialisation.

Dans le setter, self.score = value rappellerait le setter, et cela se répéterait jusqu’à RecursionError. L’accesseur présente le même risque s’il renvoie self.score. Utilise le nom de stockage distinct _score dans les deux corps.

Calculer les valeurs à leur lecture

Une propriété peut calculer une valeur à partir d’autres attributs :

    @property
    def area(self):
        return self.width * self.height

Placée dans une classe de rectangle, elle permet d’utiliser r.area. Pour une largeur de 3 et une hauteur de 4, la valeur est de 12. Faire passer la largeur à 5 amène la lecture suivante à renvoyer 20. Stocker une aire une seule fois pendant l’initialisation laisserait une ancienne valeur après un changement de largeur.

Commence par un attribut simple lorsqu’un stockage simple répond au besoin.

Points clés

  • Un tiret bas initial signale par convention un stockage interne ; il ne bloque pas l’accès.
  • La déformation des noms change un nom d’attribut pour éviter les conflits.
  • Une propriété exécute un accesseur avec la syntaxe d’un attribut.
  • Un setter peut valider l’initialisation et les affectations ultérieures à un seul endroit.
  • Utilise un attribut de stockage distinct pour éviter d’appeler la propriété récursivement.
  • Une propriété calculée détermine sa valeur lors de sa lecture.
4

Utiliser les méthodes spéciales pour bien afficher et comparer les objets

Un objet utile doit s’intégrer naturellement aux expressions Python courantes. Ses méthodes spéciales peuvent fournir un texte lisible, une égalité de valeur, un ordre, des résultats arithmétiques ou une longueur. Apprends quelle méthode Python demande dans chaque situation et ce que cette méthode doit renvoyer. Prête une attention particulière à l’affichage dans les conteneurs, à la stabilité des clés de dictionnaire et à la différence entre une opération et sa forme réfléchie.

4.1 Donner aux objets des représentations textuelles lisibles

Un objet est plus facile à examiner lorsqu’il peut décrire ses propres données. Les méthodes spéciales permettent à Python de demander cette description au moyen d’actions familières comme print, str et repr. Leurs noms commencent et se terminent par deux tirets bas et sont souvent appelés noms dunder.

Choisir un texte pour deux publics

__str__ fournit un texte lisible aux utilisateurs d’un programme. __repr__ fournit une description précise aux programmeurs. Les deux renvoient des chaînes de caractères ; aucune ne doit afficher son résultat au lieu de le renvoyer.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"({self.x}, {self.y})"

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p = Point(3, 4)
print(p)        # (3, 4)
print(repr(p))  # Point(3, 4)

str(p) renvoie la chaîne "(3, 4)". repr(p) renvoie "Point(3, 4)". Lorsque c’est possible, une représentation ressemble à un appel de classe qui recréerait la valeur. Cela reste du texte : appeler repr ne crée pas en soi un autre point.

Sans représentation personnalisée, une instance ordinaire affiche généralement un nom de classe et un identifiant qui ressemble à une adresse. Cela aide à distinguer les objets mais ne montre pas leurs données utiles. Tu appelles rarement ces méthodes spéciales directement ; définis-les une fois et utilise les opérations d’affichage ordinaires de Python.

Prévoir ce qu’affiche un conteneur
ExpressionReprésentation demandée
print(p) ou str(p)__str__
repr(p)__repr__
f"{p}"La forme textuelle habituelle de cette classe
f"{p!r}"__repr__
print([p])L’affichage de la liste, qui utilise repr pour son élément

Avec la classe ci-dessus, print([Point(3, 4), Point(0, 0)]) affiche [Point(3, 4), Point(0, 0)]. La liste utilise les représentations de ses éléments, même si chaque élément possède aussi __str__.

Si tu définis __repr__ seule, la méthode __str__ par défaut l’utilise aussi. Définir __str__ seule ne change pas repr ni l’affichage des conteneurs. Si tu ajoutes une seule méthode d’affichage, __repr__ sert donc dans davantage de situations d’examen.

Éviter les ambiguïtés dans les champs textuels

Pour un livre qui stocke déjà title, author et year, ces méthodes donnent deux représentations utiles :

    def __str__(self):
        return f"{self.title} by {self.author} ({self.year})"

    def __repr__(self):
        return f"Book({self.title!r}, {self.author!r}, {self.year})"

Pour le livre dont le titre est "Dune", l’auteur "Frank Herbert" et l’année 1965, la première méthode donne "Dune by Frank Herbert (1965)". La seconde donne "Book('Dune', 'Frank Herbert', 1965)". !r demande les représentations des champs textuels, guillemets compris.

Un corps qui contient seulement print(self.x) renvoie None, donc str(p) échoue avec TypeError. Renvoyer un entier échoue aussi. Construis le texte nécessaire, puis renvoie cette chaîne.

Points clés

  • __str__ fournit un texte destiné aux utilisateurs ; __repr__ fournit une représentation utile aux programmeurs.
  • Les conteneurs affichent leurs éléments avec repr.
  • __repr__ peut fournir la forme textuelle par défaut ; __str__ ne remplace pas repr.
  • Les deux méthodes doivent renvoyer des chaînes de caractères.
  • Utilise !r dans une f-string pour afficher les représentations des champs textuels entre guillemets.

4.2 Comparer et combiner des objets avec des opérateurs

Les méthodes spéciales relient une classe aux opérateurs et aux fonctions natives. Définis seulement des opérations dont le sens est clair pour ce type : additionner des vecteurs est compréhensible ; ajouter un joueur à une équipe est plus clair avec team.add_player(player).

Comparer les valeurs et assurer la fiabilité du hachage

Par défaut, deux instances ordinaires distinctes ne sont pas égales. Ajoute l’égalité de valeur à une classe de points avec :

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return self.x == other.x and self.y == other.y

Deux points en (3, 4) sont maintenant considérés comme égaux même si leurs identités diffèrent. Sur les listes, in, count, index et remove peuvent trouver de telles valeurs égales. Le résultat habituel de != découle de __eq__.

NotImplemented est une valeur de retour qui permet à Python d’essayer l’autre opérande ou une solution de repli. Ce n’est pas une exception à lever. Lorsqu’aucun des deux opérandes ne prend en charge l’égalité de valeur, == se rabat sur l’identité ; un point et la chaîne "3,4" ne sont pas considérés comme égaux.

Définir __eq__ sans __hash__ rend les instances non hachables. Elles ne peuvent alors pas être membres d’un ensemble ni clés d’un dictionnaire. Des objets égaux doivent avoir des valeurs de hachage égales, et celle d’une clé doit rester stable. Si les attributs comparés ne changent jamais, une méthode de hachage correspondante peut renvoyer hash((self.x, self.y)). Garde les objets de valeur mutables non hachables lorsque leurs champs servant à l’égalité peuvent changer.

Définir un ordre

__lt__ fournit <. Une classe d’étudiants peut comparer les scores avec return self.score < other.score. Les scores 88, 72 et 95 sont alors triés dans l’ordre 72, 88, 95. min choisit le plus bas ; max choisit le plus haut en utilisant > et sa comparaison réfléchie <.

Avec __lt__ seule, a > b peut utiliser b < a. Python ne déduit pas <= ni >= de cette définition ; ces opérateurs ont besoin de leurs propres méthodes. Si un ordre n’est utile que pour une seule tâche, passe plutôt une fonction clé :

def by_score(student):
    return student.score

ordered = sorted(students, key=by_score)
Renvoyer de nouveaux résultats arithmétiques

Dans une classe de vecteurs avec des attributs x et y :

    def __add__(self, other):
        if not isinstance(other, Vector):
            return NotImplemented
        return Vector(self.x + other.x, self.y + other.y)

    def __mul__(self, factor):
        if not isinstance(factor, (int, float)):
            return NotImplemented
        return Vector(self.x * factor, self.y * factor)

    def __rmul__(self, factor):
        return self * factor

Vector(1, 2) + Vector(3, 4) produit un nouveau vecteur (4, 6). Multiplier le premier vecteur par 3 produit (3, 6), sans le modifier. __mul__ gère le vecteur à gauche ; __rmul__ prend en charge un nombre à gauche. Python ne suppose pas que les deux ordres ont le même sens.

Fournir une longueur qui a un sens

Pour une liste de lecture qui stocke self.songs, définis __len__ pour renvoyer len(self.songs). Le résultat doit être un entier positif ou nul. len(playlist) compte alors les chansons. Dans cette classe, une longueur nulle rend aussi if playlist: faux ; une méthode __bool__ personnalisée aurait priorité sur la longueur lors du test de vérité.

Points clés

  • __eq__ définit l’égalité de valeur ; renvoie NotImplemented pour les types d’opérandes non pris en charge.
  • Des objets hachables égaux doivent avoir des valeurs de hachage égales et stables.
  • __lt__ permet le tri mais ne fournit ni <= ni >=.
  • Utilise une fonction clé pour définir un ordre de tri propre à une tâche.
  • Les méthodes arithmétiques peuvent créer de nouveaux objets sans modifier les opérandes.
  • __rmul__ permet la multiplication avec l’objet à droite.
  • __len__ renvoie un entier positif ou nul et peut fournir la valeur de vérité lorsqu’il n’existe pas de __bool__.
5

Construire des programmes à partir d’objets qui coopèrent

Les programmes plus grands relient des objets qui ont des tâches différentes. Un panier peut demander des sous-totaux aux articles ; un compte peut demander à un autre de recevoir un dépôt. Suis les références et l’ordre des appels pour voir quel état change et où une erreur arrête l’exécution. Utilise ensuite ces idées pour choisir une conception claire, préparer une petite classe et tester son comportement public par étapes accessibles.

5.1 Construire des objets qui contiennent d’autres objets

Les attributs d’un objet peuvent désigner d’autres objets. Un panier contient des articles, une bibliothèque contient des livres et un cours compte des étudiants. La composition construit un objet plus grand à partir d’objets plus petits grâce à cette relation de possession.

Donner à chaque objet sa propre responsabilité
class Item:
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

    def subtotal(self):
        return self.price * self.quantity

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add(self, item):
        if not isinstance(item, Item):
            raise TypeError("a cart holds Item objects")
        self.items.append(item)

    def total(self):
        total = 0
        for item in self.items:
            total += item.subtotal()
        return total

Le panier possède une liste distincte créée pendant l’initialisation. Sa méthode add vérifie l’objet reçu avant de l’ajouter. cart.add("pen") lève TypeError au lieu de stocker du texte qui provoquerait un échec lorsqu’un sous-total serait demandé.

Crée les articles avant ou pendant les appels :

cart = ShoppingCart()
cart.add(Item("notebook", 2.50, 4))
cart.add(Item("pen", 1.25, 3))
print(cart.total())         # 13.75
print(cart.items[0].name)   # notebook

Les sous-totaux sont 10.0 et 3.75 ; le panier les additionne pour obtenir 13.75. La chaîne cart.items[0].name accède d’abord à la liste du panier, puis à son premier objet, puis au nom de cet objet.

Déléguer le travail à l’objet qui sait le faire

Le panier demande subtotal() à chaque article. C’est la délégation : un objet appelle la méthode d’un autre objet pour effectuer une partie d’une tâche. Si une remise sur quantité change le calcul d’un sous-total, cette règle appartient à Item.subtotal. Le panier peut continuer à additionner les sous-totaux renvoyés sans dupliquer les règles de prix.

Passer Item au lieu de Item(...) transmet la classe, pas un article acheté. La vérification de type rejette cet objet classe. Effectue tous les ajouts par la méthode qui vérifie ; un accès direct à cart.items.append(...) contournerait add.

Rechercher un objet

Ajoute cette méthode dans ShoppingCart :

    def find(self, name):
        for item in self.items:
            if item.name == name:
                return item
        return None

La première correspondance renvoie l’article existant. En l’absence de correspondance, la méthode renvoie None. Vérifie found is None avant d’utiliser found.name ; None n’a pas un tel attribut. Une méthode __len__ qui renvoie len(self.items) pourrait aussi permettre à len(cart) de compter les objets articles stockés, tandis que __str__ pourrait assembler les lignes des articles en un ticket.

Suivre les références dans les collections

Une liste stocke des références : placer le même article dans deux paniers ne le copie donc pas. Mettre à jour le prix de cet article influe sur le prochain total calculé par les deux paniers. Des listes de panier distinctes évitent le partage involontaire de la liste, mais ne rendent pas indépendants les objets qu’elles contiennent. Crée des articles distincts lorsque les paniers doivent contenir des données indépendantes.

Points clés

  • La composition stocke des objets dans un autre objet.
  • La délégation demande à un objet contenu d’effectuer sa partie d’une tâche.
  • Vérifie un objet avant de le stocker lorsque les méthodes ultérieures exigent son type.
  • Une recherche peut renvoyer l’objet correspondant ou None.
  • Des conteneurs distincts peuvent néanmoins désigner le même objet contenu.

5.2 Faire interagir des objets au moyen de méthodes

Les objets peuvent être des arguments et des valeurs de retour de méthodes, comme les nombres ou les chaînes de caractères. Suis séparément l’objet récepteur, les arguments et le résultat : une méthode peut modifier un objet existant, renvoyer un nouvel objet ou simplement communiquer une valeur.

Utiliser un autre objet comme entrée

Dans une classe de points avec x et y, une méthode peut lire les deux points :

    def midpoint(self, other):
        return Point((self.x + other.x) / 2,
                     (self.y + other.y) / 2)

Pour p = Point(3, 4) et q = Point(1, 0), middle = p.midpoint(q) crée un troisième point en (2.0, 2.0). Pendant l’appel, self désigne p et other désigne q. La méthode calcule les coordonnées et construit un nouveau point ; elle n’affecte rien à aucun des deux points d’origine.

Avec import math, une méthode de distance peut renvoyer math.hypot(self.x - other.x, self.y - other.y). Point(0, 0).distance_to(Point(3, 4)) donne alors 5.0. Cet appel renvoie un nombre, tandis que midpoint renvoie un objet.

Laisser chaque objet récepteur gérer ses modifications

Dans une classe de comptes avec des méthodes withdraw et deposit qui effectuent des vérifications :

    def transfer_to(self, other, amount):
        self.withdraw(amount)
        other.deposit(amount)

Si Maya a 100 et Leo a 20, maya.transfer_to(leo, 30) retire d’abord une somme du compte de Maya, laissant 70, puis la dépose sur celui de Leo, donnant 50. Dans le premier appel, self est le compte de Maya. Dans l’appel de dépôt, son propre self est le compte de Leo.

Un transfert ultérieur de 500 échoue dans withdraw parce que Maya n’a que 70. L’exécution s’arrête avant deposit, donc les deux soldes gardent leurs valeurs précédentes. L’ordre des appels compte. Python n’annule pas un changement d’état antérieur si un appel ultérieur lève une exception ; cette courte méthode suppose que le compte destinataire puisse accepter le montant du dépôt.

Appeler other.deposit(amount) exécute les règles du compte destinataire à un seul endroit. Modifier directement other.balance les contournerait.

Suivre les objets, pas seulement les noms de variables

Dessine chaque instance sous forme d’une boîte contenant ses attributs. Dessine une flèche de chaque variable vers sa boîte. Lors d’un appel, détermine quelle boîte reçoit self et quelles boîtes sont passées comme arguments. Mets à jour la boîte concernée après chaque ligne qui modifie l’état avec succès. Deux flèches vers une même boîte signifient qu’une modification par l’un ou l’autre nom atteint le même objet.

Une simple fonction peut aussi agir sur plusieurs objets. total_balance(accounts) peut parcourir les comptes et additionner leurs soldes. Ce calcul sur toute la liste n’a pas besoin d’appartenir à un seul compte.

Représenter une relation absente

Un livre peut commencer avec self.borrower = None, puis stocker plus tard un objet représentant un adhérent. Avant de lire un attribut de l’adhérent, vérifie la relation :

if book.borrower is not None:
    print(book.borrower.name)

Sinon, book.borrower.name échoue lorsque le livre n’a pas d’emprunteur. Les classes peuvent être définies au-dessus d’une fonction main() qui crée et relie les objets. Appeler main() sous if __name__ == "__main__": empêche ces actions de s’exécuter lorsque les classes sont importées.

Points clés

  • Identifie l’objet récepteur comme self et l’argument objet par son propre paramètre.
  • Une méthode peut renvoyer un nouvel objet tout en laissant ses entrées inchangées.
  • Les appels ont lieu dans l’ordre ; une exception arrête les appels suivants mais n’annule pas les modifications précédentes.
  • Utilise les méthodes publiques d’un autre objet pour appliquer ses règles.
  • Vérifie l’absence de None avant de suivre une relation facultative vers un objet.
  • Une fonction peut organiser des calculs qui portent sur plusieurs objets.

5.3 Décider quand une classe rend un programme plus clair

Une classe a sa place lorsque des données liées et des opérations répétées sont plus faciles à comprendre ensemble. Tu n’as pas besoin de transformer chaque fonction ou enregistrement en classe. Choisis la structure qui rend les responsabilités du programme claires.

Chercher les données et le comportement qui vont ensemble

Les classes sont utiles lorsque plusieurs fonctions acceptent sans cesse le même groupe de valeurs, que de nombreux enregistrements partagent une structure et que les changements d’état doivent suivre des règles communes. Plusieurs comptes qui permettent tous des dépôts et des retraits s’y prêtent bien. Un calcul unique peut rester une fonction. Un enregistrement fixe en lecture seule peut rester un tuple ou un dictionnaire.

Une classe qui ne contient que l’initialisation et une opération effectuée une seule fois ajoute souvent plus de formalisme que de structure utile. Demande-toi ce que représente une instance et quelles actions répétées lui appartiennent. Si tu ne peux pas répondre simplement à ces questions, reconsidère la conception.

Placer une opération près de ses données

Un compte fondé sur un dictionnaire pourrait commencer ainsi :

account = {"owner": "Maya", "balance": 50}

def deposit(account, amount):
    account["balance"] += amount

La version avec classe peut placer la même opération à côté de son état et de ses règles :

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("deposit must be positive")
        self.balance += amount

    def __repr__(self):
        return f"BankAccount({self.owner!r}, {self.balance})"

Après acct = BankAccount("Maya", 50) et acct.deposit(25), sa représentation est BankAccount('Maya', 75). La règle qui impose un dépôt positif s’exécute maintenant chaque fois que les appelants utilisent cette méthode. Lire account["balence"] lèverait KeyError ; lire acct.balence lève AttributeError. Une classe n’élimine pas les fautes d’orthographe, mais elle donne aux données et à leurs opérations un emplacement nommé.

Concevoir à partir d’une courte description

Pour « Une bibliothèque prête des livres à ses adhérents. Un adhérent peut emprunter au maximum 3 livres », les noms suggèrent Book, Member et Library. Les verbes suggèrent des méthodes d’emprunt et de retour.

  1. Liste ce que chaque instance connaît : le titre, l’auteur et l’emprunteur d’un livre ; le nom et les livres empruntés d’un adhérent ; les livres et les adhérents d’une bibliothèque.
  2. Identifie les règles : un livre ne peut pas être déjà emprunté et un adhérent ne peut pas dépasser MAX_LOANS = 3.
  3. Place chaque règle dans l’opération qui modifie la relation, comme Library.check_out(book, member).
  4. Écris l’initialisation et une représentation utile, puis ajoute et teste une action à la fois.

Des titres différents nécessitent des instances différentes de Book, pas des classes distinctes nommées d’après chaque titre. Utilise des noms au singulier pour les objets individuels, comme Book ; nomme une collection d’après la collection, comme Library. Garde la tâche de chaque classe suffisamment claire pour la décrire en une phrase.

Tester le comportement observable
acct = BankAccount("Maya", 50)
acct.deposit(25)
assert acct.balance == 75

assert lève AssertionError si sa condition est fausse. Teste aussi les actions refusées : intercepte l’exception attendue et vérifie que l’état n’a pas changé. Utilise une validation explicite avec raise pour les entrées de l’appelant ; les assertions sont des vérifications effectuées pendant le développement et peuvent être désactivées lorsque Python s’exécute avec l’optimisation.

Points clés

  • Choisis une classe lorsque réunir un état et des opérations répétées qui vont ensemble est utile.
  • Garde un calcul simple sous forme de fonction lorsqu’aucun état durable d’objet n’est nécessaire.
  • Prévois les attributs à partir des données et les méthodes à partir des actions et des règles.
  • Modélise les différentes valeurs de données comme des instances d’une même classe.
  • Teste les mises à jour réussies comme les modifications refusées.

Classes et objets en Python : exemples

5 Leçons Interactives

1

Des valeurs familières à ta première classe

11 étapes
Suis les appels de méthodes et les références partagées, puis crée deux objets avec leurs propres attributs.
  1. 1
    IntroductionCommencer par des objets que tu connais

    Une liste, une chaîne de caractères et un nombre sont tous des objets. Leurs types déterminent les opérations qu’ils permettent, et chaque valeur a sa propre identité.

    Commence par suivre une méthode de chaîne et une méthode de liste. Utilise ensuite les mêmes idées pour créer une classe simple dont les instances stockent des données différentes. Continue à te poser deux questions : quel objet ce nom désigne-t-il et que renvoie cet appel ?

  2. 2
    ExempleDemander à une valeur son type

    Python peut t’indiquer de quel type d’objet une valeur relève.

    text = "river"
    values = [4, 2]
    print(type(text))              # <class 'str'>
    print(isinstance(values, list))  # True
    

    La chaîne de caractères et la liste proposent des méthodes différentes parce qu’elles ont des types différents. Tu peux utiliser dir(values) pour examiner les noms disponibles sur la liste, notamment les méthodes ordinaires et les méthodes spéciales.

  3. 3
    ExempleSuivre une chaîne renvoyée

    Une méthode de chaîne peut produire une nouvelle valeur sans remplacer l’originale.

    name = "robin"
    result = name.title()
    print(result)  # Robin
    print(name)    # robin
    

    title() renvoie une nouvelle chaîne. L’affectation stocke ce résultat sous result, tandis que name désigne toujours "robin". Écrire name = name.title() ferait au contraire désigner à name la chaîne renvoyée.

  4. 4
    ExempleSuivre la modification en place d’une liste

    Compare l’appel sur la chaîne avec un tri de liste.

    values = [4, 2]
    result = values.sort()
    print(values)  # [2, 4]
    print(result)  # None
    

    La liste elle-même change et la valeur de retour est None. Réaffecter l’appel à values remplacerait la référence à la liste associée à ce nom par None. Utilise values.sort() seul lorsque tu veux modifier la liste existante.

  5. 5
    DéfiOù l’ajout apparaît-il ?

    Prévois les deux listes affichées.

    a = [7]
    b = a
    c = [7]
    b.append(2)
    print(a, c)
    

    ---reveal---

    La sortie est [7, 2] [7]. a et b désignent une même liste : l’ajout par b modifie donc la liste vue par a. c contient une liste distincte. Les listes étaient initialement égales, mais a is c était faux.

  6. 6
    ConseilDessiner les références avant de suivre les modifications

    Dessine une boîte pour chaque liste créée séparément. Fais pointer a et b vers la première boîte, et c vers la seconde. Un ajout modifie la boîte ; une affectation modifie ce que désigne un nom.

    Distingue aussi une méthode de son appel : name.title donne un objet méthode, tandis que name.title() l’exécute. Les parenthèses peuvent faire la différence entre disposer d’un comportement et obtenir son résultat.

  7. 7
    AnalogieUn formulaire, plusieurs copies remplies

    Imagine une classe comme un formulaire vierge qui décrit les informations contenues dans un type d’objet. Une instance est une copie remplie avec ses propres données.

    Une classe Badge peut représenter tous les badges d’un programme. Une instance contient le nom d’un visiteur ; une autre contient un nom différent. La classe décrit le type d’objet commun. Tu n’as pas besoin d’une nouvelle classe pour chaque valeur inscrite sur le formulaire.

  8. 8
    ExempleDéfinir le type et créer deux instances

    Utilise une classe minimale pendant que tu apprends comment fonctionnent les instances.

    class Badge:
        pass
    
    first = Badge()
    second = Badge()
    first.name = "Rae"
    second.name = "Ivo"
    

    pass fournit un corps vide. Chaque appel de classe crée une instance, et les affectations avec un point stockent des données sur l’objet choisi. first is second est faux. first = Badge donnerait à la place un autre nom à la classe sans créer d’instance.

  9. 9
    DéfiTrouver un attribut mal orthographié

    Continue avec les deux badges :

    first.nmae = "Tess"
    print(first.name, second.name)
    

    Qu’est-ce qui s’affiche ?

    ---reveal---

    La sortie est Rae Ivo. L’affectation crée un attribut distinct nommé nmae ; elle ne modifie pas name. Lire un attribut qui n’existe pas lève AttributeError, mais l’affectation à un nouveau nom est autorisée. Si une lecture donne un résultat surprenant, vérifie l’orthographe à l’affectation comme à la lecture.

  10. 10
    DéfiCombiner instances et alias

    Continue avec first et second de l’exemple des badges :

    copy_name = first
    copy_name.name = "Noor"
    print(first.name, second.name)
    

    ---reveal---

    La sortie est Noor Ivo. copy_name est un alias du premier badge, donc l’affectation modifie cet objet. Le second badge reste distinct. La création d’instances détermine le nombre d’objets existants ; l’affectation de noms détermine les objets que ces noms désignent.

  11. 11
    RécapitulatifSuivre un autre programme sur papier

    Essaie un court programme avec deux appels de classe et un troisième nom affecté à la première instance. Donne des attributs différents aux objets, puis modifie-en un par l’intermédiaire de l’alias.

    Avant de l’exécuter, dessine les boîtes des objets et prévois chaque lecture. Si une prévision diffère de la sortie, examine l’orthographe des attributs et les flèches allant des noms aux objets. Cela permet de distinguer une erreur d’initialisation d’une erreur de référence.

2

Construire des objets avec des données initiales fiables

14 étapes
Initialise une petite collection de tâches, distingue les paramètres partagés des données propres à chaque objet et rejette une initialisation invalide.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionInitialiser une collection de tâches
  2. 2
    ExplicationFournir un initialiseur à Python
  3. 3
    ExempleRelier un appel à ses paramètres
  4. 4
    DéfiRepérer l’attribut manquant
  5. 5
    ExempleAccepter une liste initiale facultative
  6. 6
    ConseilVérifier la portée de la copie
  7. 7
    ExplicationAjouter des données sur la classe
  8. 8
    ExempleSuivre une affectation qui masque un attribut
  9. 9
    DéfiCorriger le compteur partagé
  10. 10
    ExplicationRejeter une limite inutilisable
  11. 11
    ExempleChoisir l’erreur et la façon de reprendre
  12. 12
    DéfiL’affichage rejette-t-il l’initialisation ?
  13. 13
    DéfiConserver l’objet précédent en cas d’échec
  14. 14
    RécapitulatifVérifier l’initialisation en tant qu’appelant
3

Contrôler l’état avec des méthodes et des propriétés

16 étapes
Construis un compteur de stock avec vérifications, suis un alias et expose un accès aux attributs avec validation.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionRendre un compteur de stock utile
  2. 2
    ExempleCommencer avec une quantité stockée
  3. 3
    ExplicationLaisser l’objet récepteur fournir self
  4. 4
    DéfiRendre un résultat réutilisable
  5. 5
    ExempleAjouter du stock après vérification de la quantité
  6. 6
    ExempleRejeter un retrait avant la soustraction
  7. 7
    DéfiSuivre une modification par un alias
  8. 8
    LienCopier l’état au bon niveau
  9. 9
    ExplicationSignaler le stockage interne
  10. 10
    ExempleLire par une propriété
  11. 11
    ExempleAutoriser une affectation publique vérifiée
  12. 12
    DéfiÉviter un setter récursif
  13. 13
    Point cléCalculer une valeur à sa lecture
  14. 14
    DéfiCombiner un setter et un alias
  15. 15
    ConseilLire ensemble la définition et l’appel
  16. 16
    RécapitulatifTester une opération à la fois
4

Adapter les objets aux expressions Python

17 étapes
Donne à un couple de coordonnées des affichages, des comparaisons, des opérations arithmétiques et un comportement natif clairs et utiles.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionChoisir ce que doit représenter un objet
  2. 2
    ExempleCommencer par un point
  3. 3
    ExempleAjouter un texte pour l’examen
  4. 4
    DéfiPrévoir l’affichage d’une liste
  5. 5
    ConseilRenvoyer du texte dans les deux méthodes d’affichage
  6. 6
    ExempleComparer les valeurs des coordonnées
  7. 7
    Point cléLaisser Python gérer les comparaisons non prises en charge
  8. 8
    DéfiDes couples égaux peuvent-ils aller dans un ensemble ?
  9. 9
    ExempleChoisir un ordre selon les coordonnées
  10. 10
    LienUn ordre peut être propre à une tâche
  11. 11
    ExempleAdditionner sans modifier les opérandes
  12. 12
    ExemplePrendre en charge les deux ordres de multiplication
  13. 13
    DéfiDistinguer un nouveau résultat d’un alias
  14. 14
    ExplicationUtiliser la longueur pour une collection
  15. 15
    DéfiPrévoir la branche pour une file vide
  16. 16
    ConseilGarder le sens visible
  17. 17
    RécapitulatifVérifier chaque expression séparément
5

Relier des objets aux tâches claires

15 étapes
Construis une petite commande à partir d’objets représentant des lignes, suis les références partagées et teste une opération refusée.

Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.

  1. 1
    IntroductionConstruire une commande à partir de parties plus petites
  2. 2
    ExempleDonner à une ligne son propre calcul
  3. 3
    ExempleStocker des lignes dans une commande
  4. 4
    ExempleDéléguer le calcul de chaque coût
  5. 5
    DéfiSuivre une ligne partagée par deux commandes
  6. 6
    ConseilVérifier l’absence de résultat de recherche
  7. 7
    ExplicationPasser un objet à une autre méthode
  8. 8
    ExempleSuivre l’objet récepteur et le résultat
  9. 9
    DéfiOù s’arrête une combinaison qui échoue ?
  10. 10
    LienSuivre les appels qui modifient deux objets
  11. 11
    ExplicationDéterminer quels comportements vont ensemble
  12. 12
    DéfiÉviter une classe pour chaque produit
  13. 13
    ExempleTester le point d’entrée public avec vérification
  14. 14
    DéfiChoisir un test pour la combinaison
  15. 15
    RécapitulatifConcevoir ta propre petite collaboration

Classes et objets en Python : QCM

50 Questions Pratiques en 5 séries

Série 1 : Objets, références et initialisation fiable10 Questions Pratiques

Question 1 sur 10

Qu’affiche ce code ?

label = "cedar"
label.upper()
print(label)
  1. Texte en majuscules : CEDAR
  2. La valeur None
  3. Texte d’origine : cedar
  4. Texte avec initiale majuscule : Cedar
Afficher la réponseMasquer la réponse
Réponse : C
upper() renvoie une nouvelle chaîne, mais cet appel ne stocke pas ce résultat. Le nom label désigne toujours la chaîne d’origine, donc la sortie est cedar. Attendre CEDAR revient à considérer la méthode de chaîne comme une mise à jour en place ; les chaînes de caractères sont immuables.

Question 2 sur 10

Qu’affiche ce code ?

a = [6, 2]
b = a
a = sorted(a)
b.append(9)
print(a, b)
  1. [2, 6] [6, 2, 9]
  2. [2, 6, 9] [2, 6, 9]
  3. [6, 2, 9] [2, 6]
  4. [2, 6] [2, 6, 9]
Afficher la réponseMasquer la réponse
Réponse : A
sorted(a) crée une nouvelle liste triée, et l’affectation fait que a la désigne. b désigne toujours la liste d’origine, donc l’ajout transforme cette liste en [6, 2, 9]. Considérer que les deux noms désignent toujours la même liste revient à oublier la réaffectation de a.

Question 3 sur 10

Que contient first.name à la fin ?

class Badge:
    pass
first = Badge()
second = Badge()
first.name = "Rae"
second.name = "Ivo"
first.nmae = "Sol"
  1. "Sol"
  2. "Ivo"
  3. Aucune valeur ; l’affectation échoue.
  4. "Rae"
Afficher la réponseMasquer la réponse
Réponse : D
first.name a reçu "Rae" et ne reçoit jamais d’autre valeur. Le nom mal orthographié nmae crée un attribut distinct sur first, tandis que second.name appartient à un autre objet. L’affectation d’un nouvel attribut est autorisée, donc la faute ne lève pas d’erreur à elle seule.

Question 4 sur 10

Un programme a besoin de trois capteurs portant des noms différents mais disposant des mêmes méthodes de lecture et de réinitialisation. Comment organiser ses classes et ses objets ?
  1. Une classe de capteur par nom, avec un objet chacune.
  2. Une classe de capteur, avec trois instances distinctes.
  3. Une instance de capteur, avec trois noms pour la désigner.
  4. Trois classes de noms, partageant une instance de capteur.
Afficher la réponseMasquer la réponse
Réponse : B
Les capteurs partagent un même type de comportement : une classe peut donc définir leurs méthodes de lecture et de réinitialisation. Des instances distinctes peuvent contenir leurs différents noms et relevés actuels. Trois noms pour une seule instance donneraient tous accès au même état : réinitialiser l’un réinitialiserait donc le capteur partagé.

Question 5 sur 10

Qu’affiche ce code ?

class Counter:
    def __init__(self, start=4):
        self.value = start
c = Counter(9)
print(c.value)
  1. 4
  2. 0
  3. 9
  4. None
Afficher la réponseMasquer la réponse
Réponse : C
Python fournit le nouvel objet comme self et passe 9 comme start. L’initialiseur stocke cette valeur dans self.value, donc la sortie est 9. La valeur par défaut 4 n’est utilisée que lorsque l’appelant omet start ; elle ne remplace pas un argument explicite.

Question 6 sur 10

Pourquoi la dernière ligne échoue-t-elle ?

class Lamp:
    def __init__(self, brightness):
        brightness = brightness
lamp = Lamp(7)
print(lamp.brightness)
  1. L’affectation ne modifie qu’un paramètre local.
  2. L’appel de classe laisse le paramètre indéfini.
  3. L’attribut est stocké sur la classe à la place.
  4. L’initialiseur doit renvoyer la luminosité.
Afficher la réponseMasquer la réponse
Réponse : A
brightness = brightness réaffecte le paramètre local et ne crée jamais d’attribut sur la lampe. La lecture lève donc AttributeError ; l’initialisation devrait stocker self.brightness = brightness. Renvoyer le nombre depuis __init__ provoquerait une TypeError distincte, sans créer l’attribut.

Question 7 sur 10

Qu’affiche ce code ?

class Folder:
    def __init__(self, tags=[]):
        self.tags = tags
a = Folder()
b = Folder()
a.tags.append("urgent")
a.tags = []
print(b.tags, a.tags)
  1. [] []
  2. ['urgent'] ['urgent']
  3. [] ['urgent']
  4. ['urgent'] []
Afficher la réponseMasquer la réponse
Réponse : D
Les arguments omis font que les deux objets stockent initialement l’unique liste par défaut. L’ajout place "urgent" dans cette liste partagée ; l’affectation ultérieure donne une nouvelle liste vide à a seulement. Attendre deux listes vides revient à confondre le remplacement d’une référence d’attribut avec le fait de vider l’objet partagé d’origine.

Question 8 sur 10

Qu’affiche ce code ?

class Ticket:
    fee = 3
x = Ticket()
y = Ticket()
x.fee = 8
Ticket.fee = 5
print(x.fee, y.fee)
  1. 5 5
  2. 8 5
  3. 8 3
  4. 3 5
Afficher la réponseMasquer la réponse
Réponse : B
Affecter une valeur à x.fee crée un attribut d’instance qui masque la valeur de classe. y n’a pas de valeur propre pour cet attribut : après la mise à jour de la classe, il trouve donc 5 sur Ticket. Attendre 5 5 revient à oublier la valeur distincte stockée sur x, que l’affectation sur la classe ne remplace pas.

Question 9 sur 10

Qu’affiche ce code ?

class Job:
    count = 0
    def __init__(self):
        self.count += 1
a = Job()
Job.count = 10
b = Job()
print(a.count, b.count, Job.count)
  1. 1 11 10
  2. 11 11 10
  3. 1 1 10
  4. 1 11 11
Afficher la réponseMasquer la réponse
Réponse : A
La création de a lit le compteur de classe 0 et stocke le compteur d’instance 1. Après le passage de la valeur de classe à 10, la création de b stocke sa propre valeur 11 ; aucun des deux initialiseurs ne met à jour Job.count. Le résultat 1 11 11 revient à traiter l’affectation augmentée sur l’instance comme une mise à jour du compteur de classe.

Question 10 sur 10

Qu’affiche ce code ?

class Batch:
    def __init__(self, size):
        if size <= 0:
            raise ValueError("size must be positive")
        self.size = size
batch = Batch(6)
try:
    batch = Batch(0)
except ValueError:
    pass
print(batch.size)
  1. 0
  2. None
  3. 6
  4. ValueError
Afficher la réponseMasquer la réponse
Réponse : C
L’appel Batch(0) lève une exception avant de renvoyer une valeur, donc l’affectation à batch n’a jamais lieu. L’exception est interceptée et batch désigne toujours l’objet valide précédent, dont la taille est de 6. Un appel de création qui échoue ne remplace pas la référence existante de l’appelant par un résultat partiel.
Série 2 : Méthodes, changements d’état et propriétés10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Quel argument Python fournit-il comme self lorsque le programme appelle panel.area() ?
  1. L’objet classe Panel.
  2. L’objet désigné par panel.
  3. La valeur renvoyée par area.
  4. Le premier attribut de panel.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Quelle modification permet à l’appelant de calculer double ?

class Pace:
    def __init__(self, minutes):
        self.minutes = minutes
    def seconds(self):
        print(self.minutes * 60)
p = Pace(3)
double = p.seconds() * 2
  1. Appeler p.seconds sans parenthèses.
  2. Passer explicitement p à p.seconds(p).
  3. Renvoyer self depuis l’initialiseur.
  4. Renvoyer le produit dans seconds.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Un constructeur exige une liste de relevés et rejette tout autre type avant de le stocker. Quelle exception décrit le mieux le cas où un appelant passe la chaîne "12,14" ?
  1. TypeError
  2. ValueError
  3. AttributeError
  4. AssertionError
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Qu’affiche ce code ?

class Tank:
    def __init__(self):
        self.level = 5
    def add(self, amount):
        self.level += amount
a = Tank()
b = a
result = b.add(4)
print(a.level, result)
  1. 5 None
  2. 9 9
  3. 9 None
  4. 5 9
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Qu’affiche ce code ?

import copy
class Log:
    def __init__(self):
        self.entries = []
a = Log()
b = copy.copy(a)
b.entries.append("open")
b.entries = ["closed"]
print(a.entries, b.entries, a is b)
  1. [] ['closed'] False
  2. ['closed'] ['closed'] True
  3. ['open'] ['open'] False
  4. ['open'] ['closed'] False
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Une session rejette les noms qui sont vides après suppression des espaces aux extrémités. Quel corps d’initialiseur applique cette règle à name ?
  1. if not name: raise ValueError("name required")

    self.name = name.strip()

  2. name = name.strip()

    if not name: raise ValueError("name required")

    self.name = name

  3. self.name = name.strip()

    if not name: raise ValueError("name required")

  4. if name == "": raise ValueError("name required")

    self.name = name

Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Pourquoi l’affectation à reading.value appelle-t-elle ce setter de façon répétée ?

class Reading:
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, number):
        self.value = number
  1. L’affectation utilise à nouveau le nom de la propriété.
  2. L’accesseur lit un nom d’attribut différent.
  3. Le setter a besoin d’une valeur de retour explicite.
  4. Le décorateur devrait nommer la classe à la place.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Un compteur doit rester positif ou nul. Sa méthode remove(n) doit rejeter les quantités négatives ou nulles et les retraits supérieurs à la valeur actuelle. Quelle séquence laisse l’état inchangé dans les deux cas de refus ?
  1. Soustraire n ; vérifier son signe ; vérifier la nouvelle valeur.
  2. Vérifier le signe ; soustraire n ; vérifier la nouvelle valeur.
  3. Vérifier le signe ; vérifier la valeur disponible ; soustraire n.
  4. Vérifier la valeur disponible ; soustraire n ; vérifier le signe.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Qu’affiche ce code ?

class Dial:
    def __init__(self, value):
        self.value = value
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, number):
        if not 0 <= number <= 9:
            raise ValueError("out of range")
        self._value = number
d = Dial(4)
try:
    d.value = 12
except ValueError:
    pass
print(d.value)
  1. 12
  2. 4
  3. 9
  4. None
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Une classe calcule area à partir de width et height avec une propriété en lecture seule. Un objet a une largeur de 6 et une hauteur de 2 ; sa largeur reçoit ensuite la valeur 8. Que renvoie la lecture de obj.area ?
  1. 12
  2. 6
  3. 16
  4. 8
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Série 3 : Affichages lisibles et opérateurs utiles10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Une classe définit à la fois __str__ et __repr__. Quelle méthode fournit le texte d’une instance lorsqu’elle apparaît dans une liste affichée ?
  1. __str__
  2. __init__
  3. __len__
  4. __repr__
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Qu’affiche ce code ?

class Tag:
    def __init__(self, text):
        self.text = text
    def __repr__(self):
        return f"Tag({self.text!r})"
t = Tag("ok")
print(t)
  1. Tag('ok')
  2. ok
  3. Tag(ok)
  4. 'ok'
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Pourquoi str(marker) lève-t-il TypeError lorsque sa méthode __str__ se contente d’appeler print(self.code) ?
  1. Le nom de la méthode a trop de tirets bas.
  2. Le code affiché doit utiliser des apostrophes.
  3. La méthode renvoie None au lieu d’une chaîne.
  4. L’appelant doit appeler directement marker.__str__().
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Une classe mutable Label définit l’égalité selon son texte et ne définit pas __hash__. Deux étiquettes contiennent le même texte. Que se passe-t-il lorsqu’elles sont placées dans un ensemble ?
  1. Elles forment deux entrées car leurs identités diffèrent.
  2. La création de l’ensemble lève une erreur de type non hachable.
  3. Elles forment une entrée car leurs textes correspondent.
  4. La création de l’ensemble convertit leurs textes en clés.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Une classe Mark définit seulement __lt__, qui compare des scores numériques, et aucune autre comparaison personnalisée. Pour a de score 2 et b de score 7, que se passe-t-il ?
  1. a > b est faux ; a <= b est vrai.
  2. a > b lève une exception ; a <= b est vrai.
  3. a > b lève une exception ; a <= b aussi.
  4. a > b est faux ; a <= b lève une exception.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Un Vector définit __mul__ pour vector * number mais aucune méthode de multiplication réfléchie. Quelle méthode faut-il ajouter pour prendre aussi en charge number * vector ?
  1. __rmul__
  2. __add__
  3. __lt__
  4. __len__
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Une classe définit __eq__ à partir de code et définit __hash__ comme hash(self.code). Ses objets sont utilisés comme clés de dictionnaire. Quelle modification de conception rend le comportement des clés fiable ?
  1. Changer la valeur de hachage à chaque échec de recherche.
  2. Utiliser id(self) comme valeur de hachage de chaque objet.
  3. Garder code inchangé tant qu’un objet est une clé.
  4. Comparer l’identité tout en gardant le hachage de code.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Une classe Queue définit __len__ comme le nombre de tâches en attente et ne définit pas __bool__. Sans tâche en attente, que valent len(q) et bool(q) ?
  1. 0 et True
  2. 0 et False
  3. None et False
  4. 1 et True
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Qu’affiche la dernière ligne ?

class Offset:
    def __init__(self, value):
        self.value = value
    def __add__(self, other):
        return Offset(self.value + other.value)
a = Offset(6)
b = a
a = a + Offset(4)
print(a.value, b.value, a is b)
  1. 10 6 False
  2. 10 10 True
  3. 6 10 False
  4. 10 10 False
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

Rank.__lt__ ordonne les enregistrements par priorité. Un rapport doit plutôt les trier par heure de création sans changer leur ordre habituel. Quelle approche convient ?
  1. Remplacer __lt__ par une comparaison des heures pour tous les enregistrements.
  2. Donner une nouvelle classe à chaque enregistrement pour l’ordre du rapport.
  3. Remplacer d’abord la priorité de chaque enregistrement par son heure de création.
  4. Appeler sorted avec une fonction clé qui renvoie l’heure de création.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Série 4 : Objets qui coopèrent et conceptions qui restent claires10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Un Workshop stocke des objets Tool dans self.tools. Que lit workshop.tools[0].label ?
  1. Le nom propre de l’atelier.
  2. Les noms de tous les outils stockés.
  3. Le nom du premier outil stocké.
  4. Le nombre de noms stockés.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Un panier calcule son total en appelant line.cost() sur chaque ligne stockée. Une remise sur quantité change le calcul du coût de chaque ligne. Où placer cette règle ?
  1. Dans l’initialisation de la liste du panier.
  2. Dans la méthode cost de la classe des lignes.
  3. Dans chaque code appelant qui affiche un panier.
  4. Dans la boucle du panier après chaque appel.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Quel total le code affiche-t-il ?

class Line:
    def __init__(self, rate, count):
        self.rate = rate
        self.count = count
    def cost(self):
        return self.rate * self.count
x = Line(4, 3)
a = [x]
b = [x, Line(2, 5)]
a[0].count = 6
print(sum(line.cost() for line in b))
  1. 22
  2. 34
  3. 24
  4. 28
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

La méthode find(name) d’une collection renvoie un objet en cas de réussite et None en cas d’échec. Quel code appelant gère les deux cas avant de lire result.name ?
  1. Lire result.name, puis vérifier result is None.
  2. Utiliser if result.name: avant d’afficher le nom.
  3. Utiliser if type(result) is None: avant la lecture.
  4. Utiliser if result is not None: avant la lecture.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Une méthode Position.midpoint(other) renvoie une nouvelle position dont les coordonnées sont les moyennes. Si a est en (2, 8) et b en (10, 4), quel est le résultat de c = a.midpoint(b) ?
  1. c en (6, 6) ; a et b inchangés.
  2. a en (6, 6) ; c désigne a.
  3. b en (6, 6) ; c désigne b.
  4. c en (12, 12) ; a et b inchangés.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

send_to(other, n) appelle self.remove(n), puis other.add(n). remove rejette une quantité supérieure au stock de l’expéditeur avant toute modification ; add accepte les quantités positives. En partant d’un stock de 9 chez l’expéditeur et de 4 chez le destinataire, un envoi de 3 réussit, puis un envoi de 8 est tenté et son exception est interceptée. Quels stocks restent-ils ?
  1. Expéditeur : 9, destinataire : 4.
  2. Expéditeur : 6, destinataire : 15.
  3. Expéditeur : 6, destinataire : 7.
  4. Expéditeur : -2, destinataire : 15.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Un transfert soustrait du stock de l’expéditeur, puis appelle la méthode add du destinataire. Celui-ci rejette la quantité avant de modifier son propre stock. Aucun code d’annulation n’existe et l’appelant intercepte l’exception. Quel état reste-t-il ?
  1. Expéditeur inchangé ; destinataire inchangé.
  2. Stock de l’expéditeur réduit ; destinataire inchangé.
  3. Stock de l’expéditeur réduit ; stock du destinataire augmenté.
  4. Expéditeur inchangé ; stock du destinataire augmenté.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

Un script convertit une température saisie et affiche le résultat une seule fois. Il n’a besoin ni d’un état conservé ni d’un comportement d’objet répété. Quelle structure est la plus claire ?
  1. Une classe pour chaque température possible.
  2. Une classe contenant chaque étape du calcul.
  3. Plusieurs objets avec un résultat partagé.
  4. Une fonction qui renvoie la conversion.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Un système de location comporte de nombreux vélos. Chaque vélo a un nom et un état de disponibilité, ainsi que les mêmes actions de location et de retour avec vérifications. Quelle conception de classe est utile ?
  1. Une classe Bike avec un état et des méthodes vérifiées.
  2. Une classe par nom avec le code des actions copié.
  3. Un objet vélo avec tous les noms comme alias.
  4. Une classe contenant seulement des instructions d’affichage.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

L’étagère doit rejeter un troisième livre et garder ses deux livres existants. Après l’erreur interceptée ci-dessous, quelle assertion révèle l’implémentation incorrecte ?

class Shelf:
    def __init__(self):
        self.limit = 2
        self.books = []
    def add(self, title):
        self.books.append(title)
        if len(self.books) > self.limit:
            raise ValueError("shelf full")
shelf = Shelf()
shelf.add("Atlas")
shelf.add("Birch")
try:
    shelf.add("Cove")
except ValueError:
    pass
  1. assert len(shelf.books) == 3
  2. assert shelf.limit == 2
  3. assert len(shelf.books) == 2
  4. assert shelf.books[-1] == "Cove"
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Série 5 : Appliquer les idées à de nouvelles situations10 Questions Pratiques

Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.

Question 1 sur 10

Pour une liste native items, que renvoie isinstance(items, list) ?
  1. Le nombre d’éléments de la liste.
  2. Les noms de ses méthodes de liste.
  3. L’objet classe nommé list.
  4. La valeur booléenne True.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 2 sur 10

Qu’affiche ce code ?

class Tray:
    labels = []
a = Tray()
b = Tray()
a.labels.append("red")
b.labels = ["blue"]
Tray.labels.append("green")
print(a.labels, b.labels)
  1. ['red', 'green'] ['blue']
  2. ['red', 'green'] ['blue', 'green']
  3. ['red'] ['blue', 'green']
  4. ['blue', 'green'] ['blue', 'green']
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 3 sur 10

Qu’affiche ce code ?

class Kit:
    def __init__(self, parts=None):
        self.parts = [] if parts is None else list(parts)
source = [[1]]
k = Kit(source)
source.append([2])
source[0].append(3)
print(k.parts)
  1. [[1]]
  2. [[1, 3], [2]]
  3. [[1, 3]]
  4. [[1], [2]]
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 4 sur 10

Le setter d’un compteur rejette les valeurs négatives avant de stocker _reading. Son initialiseur affecte directement une valeur à self._reading, donc Meter(-2) réussit. Quelle modification permet à la création d’utiliser la même vérification que les affectations publiques ultérieures ?
  1. Ajouter une instruction de retour à l’accesseur.
  2. Effectuer self.reading = reading dans __init__.
  3. Effectuer self.reading = reading dans le setter.
  4. Supprimer la propriété et garder _reading public.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 5 sur 10

Un Key compare l’égalité uniquement selon name, et son hachage utilise aussi name. Deux instances différentes commencent avec le nom "north" ; l’une devient une clé de dictionnaire. Pourquoi le nom de cette clé doit-il rester stable ?
  1. Changer un nom change l’identité des deux objets.
  2. Tous les attributs d’instance doivent être immuables en Python.
  3. Les clés de dictionnaire sont copiées automatiquement à l’insertion.
  4. Une valeur de hachage modifiée peut empêcher de retrouver la clé stockée.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 6 sur 10

Deux clubs ont chacun une liste members distincte, mais les deux listes contiennent la même instance de Member. Un club change le nom de cet adhérent. Les clubs ont maintenant besoin de noms d’adhérents indépendants. Quelle modification faut-il faire ?
  1. Créer un objet adhérent distinct pour chaque club.
  2. Copier seulement l’une des listes externes members.
  3. Affecter une autre variable à l’adhérent partagé.
  4. Déplacer members dans le corps de la classe du club.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 7 sur 10

Une classe de messages possède une méthode __str__ utile, mais une liste de messages affichée montre toujours les descriptions d’objet par défaut. Quel ajout améliore ces éléments de liste ?
  1. Une méthode __len__ qui renvoie la longueur du texte.
  2. Une méthode __init__ qui renvoie la valeur du texte.
  3. Une méthode __repr__ qui renvoie un texte utile sur le message.
  4. Un appel supplémentaire à print dans __str__.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 8 sur 10

a.combine(b) est censé laisser les deux entrées inchangées. Son corps appelle self.parts.extend(other.parts), puis renvoie self. Quel comportement cette implémentation a-t-elle réellement ?
  1. Elle crée un nouveau conteneur avec des éléments copiés.
  2. Elle modifie a et renvoie ce même objet.
  3. Elle modifie b et renvoie ce même objet.
  4. Elle laisse les deux inchangés et renvoie None.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 9 sur 10

Dans class Vault, l’initialisation effectue self.__code = "x". Qu’est-ce qui explique que vault.__code échoue tandis que vault._Vault__code peut fonctionner ?
  1. Le code est chiffré pendant l’initialisation.
  2. Les tirets bas font de la valeur un attribut de classe.
  3. L’accesseur est appelé seulement avec des parenthèses.
  4. La déformation des noms change le nom de l’attribut stocké.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Question 10 sur 10

La méthode take d’une classe utilise assert n > 0 comme seule vérification de l’entrée de l’appelant. La règle doit rester valable même lorsque Python s’exécute avec l’optimisation activée. Quelle modification faut-il faire ?
  1. Renvoyer le résultat de l’assertion à l’appelant.
  2. Déplacer l’assertion dans la boucle appelante.
  3. Utiliser une condition explicite qui lève une exception.
  4. Placer l’assertion après la mise à jour de l’état.
Afficher la réponseMasquer la réponse

Enregistre cette unité pour voir cette réponse.

Tu bloques sur ton propre exercice ? Aide aux Devoirs

Mots-Clés

62 Mots-Clés, chacun avec sa définition

A

Accesseur
Un accesseur est une méthode qui communique des informations sur l’état d’un objet sans modifier intentionnellement cet état.
L’accesseur area() d’un rectangle lit sa largeur et sa hauteur et renvoie un nombre sans le redimensionner.
Alias
Un alias est un autre nom qui désigne un objet existant : les modifications d’un objet mutable partagé peuvent donc être vues par l’un ou l’autre nom.
Après backup = entries, un ajout par backup modifie aussi la liste accessible par entries.
Argument objet
Un argument objet passe une référence à une instance dans une fonction ou une méthode, ce qui permet au code appelé d’utiliser cet objet.
Le paramètre other dans p.midpoint(q) désigne q, dont la méthode lit les coordonnées pour calculer son résultat.
Argument par défaut
Un argument par défaut est une valeur utilisée lorsqu’un appelant omet un paramètre ; elle est évaluée lors de la définition de la fonction ou de la méthode.
Avec balance=0 dans l’initialiseur, Account("Rae") commence à zéro sans fournir d’argument de solde.
Assertion
Une assertion est une vérification de développement qui lève AssertionError si sa condition est fausse ; Python peut l’omettre lors d’une exécution optimisée.
Après un dépôt, assert acct.balance == 75 vérifie le comportement ; les entrées refusées de l’appelant nécessitent toujours une validation explicite.
Attribut
Un attribut est une valeur nommée à laquelle on accède par un objet ou une classe avec la notation pointée, comme le titre stocké d’un livre.
L’affectation book.title = "Emma" stocke un titre, qui peut ensuite être lu en évaluant book.title.
Attribut d’instance
Un attribut d’instance est stocké sur un objet particulier et donne à cette instance ses propres données sous le nom de l’attribut.
Deux pizzas peuvent chacune stocker une size différente et une liste de garnitures distincte par des affectations sur self.
Attribut de classe
Un attribut de classe est stocké sur la classe et peut être trouvé par les instances qui n’ont pas d’attribut de même nom pour le masquer.
Un paramètre MAX_TOPPINGS commun à la classe peut fournir la limite par défaut lue par de nombreux objets pizza.
Attribut de stockage
Un attribut de stockage conserve sous un nom distinct la valeur gérée par une propriété, ce qui évite un accès récursif à la propriété.
Un accesseur de score lit _score ; stocker une valeur dans score à l’intérieur de son propre setter appellerait ce setter de façon répétée.
Attribut interne
Un attribut interne est destiné à être utilisé dans l’implémentation d’une classe ; il est généralement signalé par un tiret bas initial dans son nom.
Dans une classe de compte, les appelants lisent une propriété publique balance, tandis que les méthodes de la classe gèrent la valeur stockée dans _balance.

C

Classe
Une classe définit un type Python et fournit une définition commune des attributs et du comportement de ses instances.
Une seule classe Book peut créer de nombreux livres qui stockent chacun un titre différent tout en partageant les définitions de méthodes.
Composition
La composition construit un objet plus grand à partir d’autres objets stockés dans ses attributs ou ses collections, ce qui modélise une relation de possession.
Une bibliothèque peut stocker des objets livres, chacun conservant son titre et les informations sur son emprunteur actuel.
Conception de classe
La conception de classe consiste à choisir les types, les états, les actions et les règles de validité nécessaires pour modéliser clairement un problème avec des objets qui coopèrent.
La conception d’une bibliothèque commence par les livres et les adhérents, puis place une limite d’emprunt dans une opération de prêt.
Conteneur
Un conteneur est un objet qui contient des références à d’autres objets, permettant de les stocker et de les traiter ensemble.
La liste d’articles d’un panier contient des objets articles ; suivre cart.items[0] donne accès au premier article stocké.
Copie profonde
Une copie profonde copie un objet et ses objets imbriqués, ce qui sépare l’état mutable imbriqué qu’une copie superficielle peut laisser partagé.
Si un compte stocke une liste de transactions, copy.deepcopy(account) copie aussi cette liste : un ajout à l’historique de la copie laisse donc l’original intact.
Copie superficielle
Une copie superficielle crée un nouvel objet externe tout en conservant des références aux objets imbriqués existants, qui peuvent rester partagés.
Si un compte stocke une liste de transactions, le compte créé par copy.copy(account) peut toujours partager cette liste avec l’original.

D

Déformation des noms
La déformation des noms réécrit les noms du corps d’une classe qui commencent par deux tirets bas et ne se terminent pas par deux tirets bas, afin d’y inclure le nom de la classe.
Dans Acc, self.__balance devient _Acc__balance, qui reste accessible sous le nom déformé.
Délégation
La délégation confie une partie d’une tâche à la méthode d’un autre objet, laissant cet objet appliquer les règles de ses propres données.
Un panier délègue chaque sous-total à son article, puis additionne les montants renvoyés pour obtenir le total du panier.

É

Égalité de valeur
L’égalité de valeur compare les objets selon des données pertinentes, généralement définies avec __eq__, au lieu d’exiger la même identité d’objet.
Deux points distincts de mêmes coordonnées peuvent être considérés comme égaux et correspondre lors d’un test d’appartenance à une liste.
État
L’état d’un objet est l’ensemble des données actuelles contenues dans ses attributs, que les méthodes peuvent examiner et mettre à jour pendant l’exécution du programme.
Après un dépôt accepté sur un compte qui conserve un historique, l’état du compte comprend son nouveau solde et l’entrée ajoutée à l’historique.

E

Encapsulation
L’encapsulation réunit les données d’un objet et le comportement qui les gère, en donnant aux appelants une interface claire pour effectuer des modifications valides.
La méthode de dépôt d’un compte vérifie le montant et met à jour le stockage interne sans que les appelants répètent la règle.

F

Fonction clé
Une fonction clé extrait pour chaque élément la valeur qu’une opération de tri doit comparer, choisissant ainsi un ordre pour cette tâche particulière.
Un rapport peut appeler sorted(records, key=by_date) sans changer la méthode de comparaison habituelle de la classe des enregistrements.

G

Garde du programme principal
Une garde du programme principal vérifie si un module est exécuté directement et empêche les actions de démarrage de s’exécuter lorsque le module est importé.
Place main() sous if __name__ == "__main__": pour qu’un autre programme puisse importer les classes sans démarrer le programme.

H

Hachabilité
La hachabilité permet à un objet de servir de membre d’un ensemble ou de clé de dictionnaire grâce à une valeur de hachage stable et cohérente avec sa règle d’égalité.
Une classe qui définit __eq__ sans __hash__ devient non hachable, même si ses instances fonctionnent dans une liste.

I

Identité
L’identité distingue un objet de tous les autres ; l’opérateur is vérifie si deux références désignent ce même objet.
Deux listes créées séparément peuvent vérifier a == b tandis que a is b est faux, car leurs identités diffèrent.
Initialisation
L’initialisation définit l’état initial d’un objet nouvellement créé, généralement en stockant des arguments et des valeurs initiales comme attributs.
Un initialiseur de compte peut stocker son titulaire, accepter un solde initial et créer une liste vide de transactions.
Instance
Une instance est un objet appartenant à une classe, qui contient l’état particulier associé à cet objet.
Les appels Book() et Book() créent deux instances de livres distinctes, même si elles ont toutes deux le même type.
Invariant
Un invariant est une règle qui doit rester vraie pour que l’état d’un objet soit valide, y compris après des modifications réussies.
Une classe de scores peut exiger que chaque score reste entre 0 et 100 pendant l’initialisation et les affectations ultérieures.

M

Masquage
Le masquage se produit lorsqu’un attribut de données ordinaire propre à une instance cache un attribut de classe de même nom lors de la recherche.
Après p.limit = 8, modifier Pizza.limit ne remplace pas la limite distincte déjà stockée sur p.
Méthode
Une méthode est une fonction fournie par une classe, qui peut être appelée sur un objet pour agir sur cet objet ou ses données.
L’appel "cedar".upper() exécute une méthode de chaîne, tandis que scores.append(7) exécute une méthode sur une liste.
Méthode __init__
La méthode __init__ initialise une instance après sa création par Python et doit renvoyer None lorsque l’initialisation se termine.
L’appel Counter(5) passe la nouvelle instance et 5 à son initialiseur avant de renvoyer le compteur configuré.
Méthode __lt__
La méthode __lt__ implémente la comparaison « inférieur à » et fournit un ordre que des opérations comme sorted peuvent utiliser.
Une méthode __lt__ fondée sur les scores permet de trier une liste d’objets étudiants du score le plus bas au plus élevé.
Méthode d’instance
Une méthode d’instance est une fonction définie dans une classe qui reçoit l’objet sur lequel elle est appelée, par convention dans son premier paramètre, self.
Pour r.area(), Python fournit r à la méthode afin qu’elle puisse multiplier la largeur et la hauteur de ce rectangle.
Méthode liée
Une méthode liée est un objet méthode qui mémorise à la fois la fonction et l’instance à fournir lors de l’appel de la méthode.
r.area obtient une méthode liée ; ajouter des parenthèses sous la forme r.area() l’exécute et renvoie l’aire calculée.
Méthode spéciale
Une méthode spéciale porte un nom avec deux tirets bas de chaque côté et fournit un comportement pour la syntaxe ou les opérations natives de Python.
Python demande __str__ pour convertir un objet en sa forme textuelle lisible et __len__ pour obtenir sa longueur.
Mutabilité
La mutabilité est la capacité d’un objet à modifier son contenu tout en restant le même objet, avec la même identité.
Ajouter un élément à une liste modifie la liste existante ; appliquer upper() à une chaîne renvoie une nouvelle chaîne.
Mutateur
Un mutateur est une méthode qui modifie l’état d’un objet existant, souvent en vérifiant les entrées avant de mettre à jour les attributs concernés.
Un mutateur de retrait avec vérification peut réduire le solde et enregistrer une transaction seulement après la validation du montant.

N

None
None est la valeur unique de Python pour un résultat absent ou indisponible ; elle est aussi utile pour une relation à laquelle aucun objet n’est encore affecté.
Un livre peut stocker borrower = None jusqu’à ce qu’un adhérent l’emprunte : le code vérifie donc avant de lire le nom d’un emprunteur.
Notation pointée
La notation pointée est la syntaxe qui place un point entre un objet ou une classe et le nom d’un attribut auquel on accède.
Dans cart.items, le nom avant le point identifie l’objet et celui qui le suit sélectionne son attribut items.
NotImplemented
NotImplemented est une valeur de retour spéciale qui indique qu’une méthode de comparaison ou numérique ne prend pas en charge les opérandes fournis.
La méthode d’égalité d’un point peut renvoyer NotImplemented pour une chaîne, laissant Python gérer la paire non prise en charge.

O

Objet
Un objet est une valeur Python qui possède une identité, un type et un état, ainsi que les opérations prises en charge par son type.
La chaîne "hello" et la liste [2, 5] sont toutes deux des objets, même si leurs types proposent des opérations différentes.
Objet récepteur
L’objet récepteur est l’objet avant le point dans un appel de méthode d’instance ; il est automatiquement passé comme argument self de la méthode.
Dans sender.transfer_to(receiver, 5), sender reçoit l’appel de transfert ; le compte passé en argument est le destinataire.
Opération réfléchie
Une opération réfléchie permet à l’opérande de droite de traiter une opération lorsque l’opérande de gauche ne peut pas prendre en charge la paire.
Pour 3 * vector, Python peut utiliser __rmul__ du vecteur lorsque la multiplication d’entiers ne peut pas traiter ce vecteur.

P

Paramètre self
Le paramètre self reçoit par convention l’instance courante, ce qui permet à une méthode de lire ou de modifier les attributs de cet objet.
Dans self.owner = owner, le côté gauche stocke une donnée sur l’instance et le côté droit lit un paramètre local.
Propriété
Une propriété fournit un accès contrôlé par une méthode avec la syntaxe d’un attribut, ce qui permet à une lecture de calculer un résultat ou à une affectation d’exécuter des vérifications.
Lire student.score peut appeler un accesseur, et l’affectation student.score = 90 peut appeler un setter qui effectue une validation.

R

Recherche d’attribut
La recherche d’attribut est le processus qui permet de trouver un attribut nommé ; une donnée d’instance ordinaire peut être trouvée avant une valeur de classe.
Si p n’a pas son propre limit, la lecture de p.limit peut trouver la valeur partagée limit définie sur sa classe.
Référence
Une référence relie un nom ou une valeur stockée à un objet, ce qui permet à plusieurs noms ou conteneurs d’accéder au même objet.
Placer un article dans deux listes de paniers partage cet article : modifier son prix peut donc influer sur les deux totaux calculés.
Représentation de repli
La représentation de repli permet à la conversion en chaîne par défaut d’utiliser une méthode __repr__ personnalisée lorsqu’une classe ne définit pas de __str__ personnalisée.
Ajouter seulement __repr__ peut améliorer à la fois print(point) et l’apparence du point dans une liste affichée.
Représentation officielle
Une représentation officielle est le texte sans ambiguïté renvoyé par __repr__, utilisé pour l’examen et dans l’affichage des conteneurs.
La représentation d’un livre peut montrer "Book('Emma', 'Jane Austen', 1815)", avec les champs textuels entre guillemets.
Représentation textuelle
Une représentation textuelle est le texte lisible renvoyé par __str__, normalement destiné aux utilisateurs du programme.
Un point peut s’afficher sous la forme "(3, 4)" lorsqu’il est affiché directement, sans modifier ses attributs de coordonnées numériques.
Responsabilité
La responsabilité d’une classe est la tâche claire à laquelle servent ses données et ses méthodes ; elle aide à décider quels comportements vont ensemble.
Un article calcule son propre sous-total, tandis qu’un panier organise les articles et additionne ces sous-totaux pour obtenir un total.
Résultat de recherche
Un résultat de recherche peut être l’objet existant correspondant ou une valeur spéciale comme None indiquant qu’aucune correspondance n’a été trouvée.
L’appelant vérifie found is not None avant de lire found.name dans le résultat d’une recherche d’article du panier.

S

Setter
Un setter est la méthode appelée par une affectation à une propriété ; il valide souvent la valeur proposée avant de la stocker séparément.
Un setter de score qui rejette 105 avant l’affectation à _score laisse inchangé un score précédemment valide.
Surcharge d’opérateurs
La surcharge d’opérateurs définit le sens d’un opérateur pour une classe en implémentant la méthode spéciale associée à cette opération.
La méthode __add__ d’un vecteur peut faire que a + b produise un nouveau vecteur en additionnant leurs coordonnées correspondantes.

T

Test de comportement
Un test de comportement crée des objets, effectue une action et vérifie un résultat observable, y compris l’état préservé après une entrée rejetée.
Un test de retrait intercepte l’erreur attendue et vérifie que le solde et l’historique correspondent toujours à leurs valeurs précédentes.
Type
Le type est la classe d’un objet : il définit les opérations prises en charge et le comportement des valeurs de cette catégorie.
type([2, 5]) renvoie la classe list, et isinstance([2, 5], list) confirme que la valeur est une liste.
TypeError
Une TypeError indique qu’une opération a reçu un type inadapté ou a été appelée avec des arguments que sa définition n’accepte pas.
Passer du texte à une comparaison numérique de largeur lève TypeError, car cette comparaison ne peut pas ordonner du texte et zéro.

V

Valeur de retour
Une valeur de retour est l’objet qu’un appel fournit à l’expression qui le contient, disponible pour une affectation, un calcul ou un test.
Renvoyer l’aire permet à r1.area() + r2.area() d’additionner les résultats, tandis que l’affichage seul laisse None comme résultat de chaque appel.
Valeur de vérité
La valeur de vérité est l’interprétation booléenne d’un objet dans une condition ; sans __bool__, une longueur définie comme nulle le rend faux.
Une liste de lecture vide avec __len__ et sans __bool__ personnalisée fait que if playlist: ignore son corps.
Valeur par défaut mutable
Une valeur par défaut mutable est un objet par défaut modifiable qui peut être réutilisé entre les appels et provoquer un partage involontaire s’il est stocké ou modifié.
Si items=[] est stocké directement, deux objets créés sans cet argument peuvent désigner la même liste.
Validation
La validation vérifie si des données reçues ou une modification demandée respectent les règles exigées par une classe avant de les accepter.
Un rectangle valide une largeur numérique en rejetant les valeurs nulles ou négatives avant de stocker la dimension.
ValueError
Une ValueError indique qu’un argument a un type acceptable mais que sa valeur ne fait pas partie de celles acceptées par l’opération.
Une largeur numérique de -2 peut lever ValueError avec le message "width must be positive".

À propos de cette unité

Par StudeliSignaler une erreur

Crée ton propre Matériel d'Étude

Transforme tes notes, diapos et documents en Guides d'Étude, Questions Pratiques, Flashcards et 6 autres outils.

Commencer