Question 1Où écris-tu une variable de classe ?
Variables de classe vs variables d'instance — Où vivent vraiment les valeurs
Variables de classe vs variables d'instance en Python, illustré. Vois ce qui est partagé, ce qui est par instance, et ce que self.x = ... fait vraiment.
La dernière fois on a bouclé l'utilisation pratique de __init__ — arguments requis, arguments par défaut — et on a rencontré le __del__ correspondant. Cette fois, on creuse les deux types de variables dans une classe — variables de classe et variables d'instance — et on voit ce que self.x = ... fait réellement au niveau de la mémoire.
Deux types de variables
Les variables que tu écris dans une classe Python tombent dans deux grands types :
- Variables de classe — écrites directement sous class Product:. Partagées par chaque instance.
- Variables d'instance — écrites comme self.x = ... à l'intérieur d'une méthode. Possédées individuellement par chaque instance.
Les deux se lisent avec la même syntaxe pointée apple.name, donc tu ne peux pas les distinguer à l'œil. C'est précisément pour ça qu'il faut être attentif à « où la valeur vit réellement ».
class Product:
tax_rate = 0.1 # variable de classe (partagée par tous)
def __init__(self, name):
self.name = name # variable d'instance (par instance)
apple = Product("apple")
banana = Product("banana")
tax_rate vit à un seul endroit sur Product — apple et banana regardent le même endroit. La variable d'instance name est stockée dans la mémoire propre à chaque instance.Variables de classe — Une valeur partagée par tous
Écris une variable de classe directement sous class Product:, comme tax_rate = 0.1. Le point clé est qu'elle vit en dehors de toute méthode. La classe elle-même possède une copie unique, et chaque instance construite à partir de cette classe la partage.
Tu peux la lire comme Product.tax_rate (via la classe) ou apple.tax_rate (via une instance). Pour cette dernière, Python fait la recherche dans cet ordre : vérifie d'abord l'instance, puis se rabat sur la classe.
- tax_rate = 0.1 ← variable de classe (partagée)
- name = 'apple' ← variable d'instance
- name = 'banana' ← variable d'instance
class Product:
tax_rate = 0.1 # variable de classe (partagée par tous)
def __init__(self, name, price):
self.name = name # variable d'instance (par instance)
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
print(Product.tax_rate) # 0.1 (via la classe)
print(apple.tax_rate) # 0.1 (lisible aussi via une instance)
print(banana.tax_rate) # 0.1
# Même emplacement mémoire
print(id(apple.tax_rate) == id(Product.tax_rate)) # True
Variables d'instance — Possédées par chaque instance
Une variable d'instance est créée au moment où tu écris self.x = ... à l'intérieur d'une méthode, comme un attribut exclusif à cette instance. Le plus souvent tu les initialises dans __init__ avec quelque chose comme self.name = name, mais écrire self.x = ... dans n'importe quelle autre méthode crée également une nouvelle variable d'instance à ce moment-là.
Comme chaque instance stocke ses variables d'instance dans sa propre mémoire, modifier apple.name n'a aucun effet sur banana.name.
apple.name et banana.name vivent à des emplacements mémoire différents. Réécrire apple.name n'affecte pas du tout banana.name.class Product:
def __init__(self, name, price):
self.name = name
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
print(apple.name, apple.price) # apple 150
print(banana.name, banana.price) # banana 80
# id() montre qu'ils pointent vers des endroits différents
print(id(apple.name) == id(banana.name)) # False
self.x = ... crée une variable d'instance
Suppose que Product ait une variable de classe tax_rate = 0.1, et que tu écrives apple.tax_rate = 0.05 pour une instance spécifique — que se passe-t-il ?
La réponse : la variable de classe n'est pas touchée, et une toute nouvelle variable d'instance tax_rate est créée sur apple. À partir de là, apple.tax_rate renvoie 0.05 parce que Python la trouve d'abord sur l'instance et ne se rabat jamais sur la classe. banana n'est pas affectée du tout.
class Product:
tax_rate = 0.1
def __init__(self, name, price):
self.name = name
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
# Donne à apple seul un nouveau taux de taxe
apple.tax_rate = 0.05
print(apple.tax_rate) # 0.05 (variable d'instance d'apple)
print(banana.tax_rate) # 0.1 (se rabat sur la variable de classe)
print(Product.tax_rate) # 0.1 (variable de classe inchangée)
apple et banana partagent tous deux un lookup de tax_rate=0.1 sur la classe. Rangée du bas (après apple.tax_rate = 0.05) : une nouvelle variable d'instance naît sur apple, et seul apple lit depuis elle. banana et la classe sont intacts.Ne confonds pas avec « mettre à jour une variable de classe via une instance »
apple.tax_rate = 0.05 ressemble à première vue à « mettre à jour la variable de classe », mais en fait ça crée juste une nouvelle variable d'instance sur apple. Pour modifier la variable de classe partagée elle-même, il faut affecter via la classe : Product.tax_rate = 0.05.
Quand tu veux mettre à jour une variable de classe
Quand tu as besoin de mettre à jour un état que la classe garde pour tout le monde — comme « changer le taux de taxe pour toutes les instances en même temps » ou « suivre le nombre cumulé de produits créés » — tu affectes via la variable de classe.
Ci-dessous, Product.created_count vit sur la classe elle-même et représente « le nombre de produits créés jusqu'ici ». Écrire Product.created_count += 1 à l'intérieur de __init__ met à jour le même unique endroit quelle que soit l'instance en cours de création, donc le total reste correct. Si tu écrivais self.created_count += 1 à la place, chaque instance aurait sa propre variable, ce qui anéantirait l'objectif initial du comptage.
self.x += 1, chaque instance finit avec sa propre copie, et le compteur partagé reste à 0.class Product:
created_count = 0 # variable de classe : compte cumulé de produits
def __init__(self, name):
self.name = name
Product.created_count += 1 # mise à jour via la classe
apple = Product("apple")
banana = Product("banana")
orange = Product("orange")
print(Product.created_count) # 3
print(apple.created_count) # 3 (lisible aussi via une instance)
On a démêlé les deux types de variables dans une classe, confirmé que self.x = ... crée toujours une nouvelle variable d'instance, que les variables d'instance masquent les variables de classe quand elles sont présentes, et que l'état partagé se met à jour via la classe.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Que produit le dernier print ?class P: n = 10a = P()a.n = 99b = P()print(b.n)
Question 3Pour incrémenter la variable de classe count à travers toutes les instances, qu'est-ce que tu écris à l'intérieur de __init__ ?