- Apprendre
- Informatique et numérique
- Programmation
- Python : variables, boucles, fonctions et fichiers
- Classes et objets en Python
Classes et objets en Python : programmation orientée objet
==, 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
- 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.
- Reconnaître les objets que tu utilises déjà
- 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 selfDé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 classeDé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éationVé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.
- Écrire un constructeur avec __init__ et self
- É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ésSignale 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.
- Définir et appeler des méthodes d’instance
- É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érateursDéfinis
__eq__,__lt__,__add__et__len__pour que tes objets fonctionnent avec==, le tri,+etlen(), et détermine quand un opérateur est plus clair qu’une méthode nommée.
- Donner aux objets des représentations textuelles lisibles
- 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 objetsStocke 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éthodesPasse 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 clairDé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.
- Construire des objets qui contiennent d’autres objets
Classes et objets en Python : cours
1Comprendre 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 ;iscompare 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
| Expression | Signification |
|---|---|
Book | L’objet classe, qui définit le type |
Book() | Un appel qui crée une instance |
b1.title | Le titre stocké sur ce livre |
Book.title | Un 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.
2Initialiser 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 = namestocke un paramètre comme attribut d’instance.- Crée les listes propres à chaque instance pendant l’initialisation ; utilise
Noneau lieu d’une valeur par défaut mutable. __init__doit renvoyerNone.
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.
| Affectation | Ce qui change |
|---|---|
self.size = size | Les données sur l’instance courante |
Pizza.count += 1 | Le compteur de classe partagé |
p1.MAX_TOPPINGS = 8 | Une 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
| Exception | Problème décrit |
|---|---|
ValueError | Une entrée de type acceptable a une valeur inacceptable |
TypeError | L’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.
3Donner 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
selfen 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.
4Utiliser 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
| Expression | Repré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 pasrepr.- Les deux méthodes doivent renvoyer des chaînes de caractères.
- Utilise
!rdans 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 ; renvoieNotImplementedpour 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__.
5Construire 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
selfet 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
Noneavant 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.
- 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.
- 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. - Place chaque règle dans l’opération qui modifie la relation, comme
Library.check_out(book, member). - É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
1Des valeurs familières à ta première classe
11 étapes
- 1IntroductionCommencer 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 ?
- 2ExempleDemander à 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)) # TrueLa 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. - 3ExempleSuivre 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) # robintitle()renvoie une nouvelle chaîne. L’affectation stocke ce résultat sousresult, tandis quenamedésigne toujours"robin". Écrirename = name.title()ferait au contraire désigner ànamela chaîne renvoyée. - 4ExempleSuivre 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) # NoneLa liste elle-même change et la valeur de retour est
None. Réaffecter l’appel àvaluesremplacerait la référence à la liste associée à ce nom parNone. Utilisevalues.sort()seul lorsque tu veux modifier la liste existante. - 5Dé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].aetbdésignent une même liste : l’ajout parbmodifie donc la liste vue para.ccontient une liste distincte. Les listes étaient initialement égales, maisa is cétait faux. - 6ConseilDessiner les références avant de suivre les modifications
Dessine une boîte pour chaque liste créée séparément. Fais pointer
aetbvers la première boîte, etcvers 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.titledonne un objet méthode, tandis quename.title()l’exécute. Les parenthèses peuvent faire la différence entre disposer d’un comportement et obtenir son résultat. - 7AnalogieUn 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
Badgepeut 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. - 8ExempleDé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"passfournit 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 secondest faux.first = Badgedonnerait à la place un autre nom à la classe sans créer d’instance. - 9Dé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 pasname. Lire un attribut qui n’existe pas lèveAttributeError, 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. - 10DéfiCombiner instances et alias
Continue avec
firstetsecondde l’exemple des badges :copy_name = first copy_name.name = "Noor" print(first.name, second.name)---reveal---
La sortie est
Noor Ivo.copy_nameest 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. - 11Ré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.
2Construire des objets avec des données initiales fiables
14 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionInitialiser une collection de tâches
- 2ExplicationFournir un initialiseur à Python
- 3ExempleRelier un appel à ses paramètres
- 4DéfiRepérer l’attribut manquant
- 5ExempleAccepter une liste initiale facultative
- 6ConseilVérifier la portée de la copie
- 7ExplicationAjouter des données sur la classe
- 8ExempleSuivre une affectation qui masque un attribut
- 9DéfiCorriger le compteur partagé
- 10ExplicationRejeter une limite inutilisable
- 11ExempleChoisir l’erreur et la façon de reprendre
- 12DéfiL’affichage rejette-t-il l’initialisation ?
- 13DéfiConserver l’objet précédent en cas d’échec
- 14RécapitulatifVérifier l’initialisation en tant qu’appelant
3Contrôler l’état avec des méthodes et des propriétés
16 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionRendre un compteur de stock utile
- 2ExempleCommencer avec une quantité stockée
- 3ExplicationLaisser l’objet récepteur fournir self
- 4DéfiRendre un résultat réutilisable
- 5ExempleAjouter du stock après vérification de la quantité
- 6ExempleRejeter un retrait avant la soustraction
- 7DéfiSuivre une modification par un alias
- 8LienCopier l’état au bon niveau
- 9ExplicationSignaler le stockage interne
- 10ExempleLire par une propriété
- 11ExempleAutoriser une affectation publique vérifiée
- 12DéfiÉviter un setter récursif
- 13Point cléCalculer une valeur à sa lecture
- 14DéfiCombiner un setter et un alias
- 15ConseilLire ensemble la définition et l’appel
- 16RécapitulatifTester une opération à la fois
4Adapter les objets aux expressions Python
17 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionChoisir ce que doit représenter un objet
- 2ExempleCommencer par un point
- 3ExempleAjouter un texte pour l’examen
- 4DéfiPrévoir l’affichage d’une liste
- 5ConseilRenvoyer du texte dans les deux méthodes d’affichage
- 6ExempleComparer les valeurs des coordonnées
- 7Point cléLaisser Python gérer les comparaisons non prises en charge
- 8DéfiDes couples égaux peuvent-ils aller dans un ensemble ?
- 9ExempleChoisir un ordre selon les coordonnées
- 10LienUn ordre peut être propre à une tâche
- 11ExempleAdditionner sans modifier les opérandes
- 12ExemplePrendre en charge les deux ordres de multiplication
- 13DéfiDistinguer un nouveau résultat d’un alias
- 14ExplicationUtiliser la longueur pour une collection
- 15DéfiPrévoir la branche pour une file vide
- 16ConseilGarder le sens visible
- 17RécapitulatifVérifier chaque expression séparément
5Relier des objets aux tâches claires
15 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionConstruire une commande à partir de parties plus petites
- 2ExempleDonner à une ligne son propre calcul
- 3ExempleStocker des lignes dans une commande
- 4ExempleDéléguer le calcul de chaque coût
- 5DéfiSuivre une ligne partagée par deux commandes
- 6ConseilVérifier l’absence de résultat de recherche
- 7ExplicationPasser un objet à une autre méthode
- 8ExempleSuivre l’objet récepteur et le résultat
- 9DéfiOù s’arrête une combinaison qui échoue ?
- 10LienSuivre les appels qui modifient deux objets
- 11ExplicationDéterminer quels comportements vont ensemble
- 12DéfiÉviter une classe pour chaque produit
- 13ExempleTester le point d’entrée public avec vérification
- 14DéfiChoisir un test pour la combinaison
- 15RécapitulatifConcevoir ta propre petite collaboration
Classes et objets en Python : QCM
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)
Afficher la réponseMasquer la réponse
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)
Afficher la réponseMasquer la réponse
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"
Afficher la réponseMasquer la réponse
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
Afficher la réponseMasquer la réponse
Question 5 sur 10
Qu’affiche ce code ?
class Counter:
def __init__(self, start=4):
self.value = start
c = Counter(9)
print(c.value)
Afficher la réponseMasquer la réponse
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)
Afficher la réponseMasquer la réponse
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)
Afficher la réponseMasquer la réponse
"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)
Afficher la réponseMasquer la réponse
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)
Afficher la réponseMasquer la réponse
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)
Afficher la réponseMasquer la réponse
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
self lorsque le programme appelle panel.area() ?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
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
"12,14" ?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)
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)
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
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
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
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 ?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)
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
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 ?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
__str__ et __repr__. Quelle méthode fournit le texte d’une instance lorsqu’elle apparaît dans une liste affichée ?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)
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
str(marker) lève-t-il TypeError lorsque sa méthode __str__ se contente d’appeler print(self.code) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
__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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
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) ?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)
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 ?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
Workshop stocke des objets Tool dans self.tools. Que lit workshop.tools[0].label ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
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 ?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))
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
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) ?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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
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
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
items, que renvoie isinstance(items, list) ?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)
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)
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
_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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
__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 ?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 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
class Vault, l’initialisation effectue self.__code = "x". Qu’est-ce qui explique que vault.__code échoue tandis que vault._Vault__code peut fonctionner ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
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 ?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
A
Accesseur
area() d’un rectangle lit sa largeur et sa hauteur et renvoie un nombre sans le redimensionner.Alias
backup = entries, un ajout par backup modifie aussi la liste accessible par entries.Argument objet
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
balance=0 dans l’initialiseur, Account("Rae") commence à zéro sans fournir d’argument de solde.Assertion
AssertionError si sa condition est fausse ; Python peut l’omettre lors d’une exécution optimisée.assert acct.balance == 75 vérifie le comportement ; les entrées refusées de l’appelant nécessitent toujours une validation explicite.Attribut
book.title = "Emma" stocke un titre, qui peut ensuite être lu en évaluant book.title.Attribut d’instance
size différente et une liste de garnitures distincte par des affectations sur self.Attribut de classe
MAX_TOPPINGS commun à la classe peut fournir la limite par défaut lue par de nombreux objets pizza.Attribut de stockage
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
balance, tandis que les méthodes de la classe gèrent la valeur stockée dans _balance.C
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
Conception de classe
Conteneur
cart.items[0] donne accès au premier article stocké.Copie profonde
copy.deepcopy(account) copie aussi cette liste : un ajout à l’historique de la copie laisse donc l’original intact.Copie superficielle
copy.copy(account) peut toujours partager cette liste avec l’original.D
Déformation des noms
Acc, self.__balance devient _Acc__balance, qui reste accessible sous le nom déformé.Délégation
É
Égalité de valeur
__eq__, au lieu d’exiger la même identité d’objet.État
E
Encapsulation
F
Fonction clé
sorted(records, key=by_date) sans changer la méthode de comparaison habituelle de la classe des enregistrements.G
Garde du programme principal
main() sous if __name__ == "__main__": pour qu’un autre programme puisse importer les classes sans démarrer le programme.H
Hachabilité
__eq__ sans __hash__ devient non hachable, même si ses instances fonctionnent dans une liste.I
Identité
is vérifie si deux références désignent ce même objet.a == b tandis que a is b est faux, car leurs identités diffèrent.Initialisation
Instance
Book() et Book() créent deux instances de livres distinctes, même si elles ont toutes deux le même type.Invariant
0 et 100 pendant l’initialisation et les affectations ultérieures.M
Masquage
p.limit = 8, modifier Pizza.limit ne remplace pas la limite distincte déjà stockée sur p.Méthode
"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__
__init__ initialise une instance après sa création par Python et doit renvoyer None lorsque l’initialisation se termine.Counter(5) passe la nouvelle instance et 5 à son initialiseur avant de renvoyer le compteur configuré.Méthode __lt__
__lt__ implémente la comparaison « inférieur à » et fournit un ordre que des opérations comme sorted peuvent utiliser.__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
self.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
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
__str__ pour convertir un objet en sa forme textuelle lisible et __len__ pour obtenir sa longueur.Mutabilité
upper() à une chaîne renvoie une nouvelle chaîne.Mutateur
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é.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
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.NotImplemented pour une chaîne, laissant Python gérer la paire non prise en charge.O
Objet
"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
self de la méthode.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
3 * vector, Python peut utiliser __rmul__ du vecteur lorsque la multiplication d’entiers ne peut pas traiter ce vecteur.P
Paramètre self
self reçoit par convention l’instance courante, ce qui permet à une méthode de lire ou de modifier les attributs de cet objet.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é
student.score peut appeler un accesseur, et l’affectation student.score = 90 peut appeler un setter qui effectue une validation.R
Recherche d’attribut
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
Représentation de repli
__repr__ personnalisée lorsqu’une classe ne définit pas de __str__ personnalisée.__repr__ peut améliorer à la fois print(point) et l’apparence du point dans une liste affichée.Représentation officielle
__repr__, utilisé pour l’examen et dans l’affichage des conteneurs."Book('Emma', 'Jane Austen', 1815)", avec les champs textuels entre guillemets.Représentation textuelle
__str__, normalement destiné aux utilisateurs du programme."(3, 4)" lorsqu’il est affiché directement, sans modifier ses attributs de coordonnées numériques.Responsabilité
Résultat de recherche
None indiquant qu’aucune correspondance n’a été trouvée.found is not None avant de lire found.name dans le résultat d’une recherche d’article du panier.S
Setter
score qui rejette 105 avant l’affectation à _score laisse inchangé un score précédemment valide.Surcharge d’opérateurs
__add__ d’un vecteur peut faire que a + b produise un nouveau vecteur en additionnant leurs coordonnées correspondantes.T
Test de comportement
Type
type([2, 5]) renvoie la classe list, et isinstance([2, 5], list) confirme que la valeur est une liste.TypeError
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.TypeError, car cette comparaison ne peut pas ordonner du texte et zéro.V
Valeur de retour
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é
__bool__, une longueur définie comme nulle le rend faux.__len__ et sans __bool__ personnalisée fait que if playlist: ignore son corps.Valeur par défaut mutable
items=[] est stocké directement, deux objets créés sans cet argument peuvent désigner la même liste.Validation
ValueError
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.-2 peut lever ValueError avec le message "width must be positive".À propos de cette unité
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.