Apprenez en lisant dans l'ordre

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")
Où vivent vraiment les valeurs
Productcorps de classetax_rate=0.1(var de classe)applename='apple'banananame='banana'détientlookup partagélookup partagé
La variable de classe tax_rate vit à un seul endroit sur Productapple 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.

Portée de recherche — Instance d'abord, puis classe
Product (espace des variables de classe)
  • tax_rate = 0.1 ← variable de classe (partagée)
apple (espace des variables d'instance)
  • name = 'apple' ← variable d'instance
banana (espace des variables d'instance)
  • name = 'banana' ← variable d'instance
Python recherche un attribut en vérifiant d'abord l'instance, puis en se rabattant sur la classe externe. Du point de vue de l'instance, la variable de classe agit comme un parapluie partagé au-dessus d'elle.
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

Ajoute une variable de classe et confirme que tu peux la lire à la fois depuis la classe et depuis les instances.

① Dans class Product:, ajoute une variable de classe currency = "USD" sur sa propre ligne (au-dessus de __init__).

② Écris __init__(self, name, price) qui affecte self.name = name et self.price = price.

③ Construis apple = Product("apple", 150). Exécute print(Product.currency) et print(apple.currency) et confirme qu'ils affichent la même valeur.

(Si tu l'exécutes correctement, une explication apparaîtra.)

Éditeur Python

Exécuter le code pour voir le résultat

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.

Chaque instance possède ses propres variables d'instance
apple(instance)name = 'apple'(mémoire séparée)banana(instance)name = 'banana'(mémoire séparée)détientdétient
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

Utilise id() pour ressentir que les variables d'instance sont vraiment séparées.

① Définis class Product: avec __init__(self, name, price) qui affecte self.name et self.price.

② Construis apple = Product("apple", 150) et banana = Product("banana", 80).

③ Exécute print(apple is banana) et print(id(apple.name) == id(banana.name)) et confirme que les deux sont False.

Éditeur Python

Exécuter le code pour voir le résultat

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)
Ce qui change avant vs après l'affectation
apple(pas de var d'inst)banana(pas de var d'inst)Producttax_rate=0.1apple.tax_rate=0.05★ nouveautax_rate=0.05var d'inst d'applebanana(pas de var d'inst)Producttax_rate=0.1(inchangé)lookup partagélookuplookup
Rangée du haut (avant) : 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.

Confirme en pratique que self.x = ... crée une nouvelle variable d'instance.

① Définis class Product: avec la variable de classe tax_rate = 0.1 et __init__(self, name) qui affecte self.name = name.

② Construis apple = Product("apple") et banana = Product("banana").

③ Après apple.tax_rate = 0.05, exécute print(apple.tax_rate), print(banana.tax_rate) et print(Product.tax_rate). Confirme que seul apple affiche la nouvelle valeur tandis que les autres restent à 0.1.

Éditeur Python

Exécuter le code pour voir le résultat

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.

Product.x += 1 vs self.x += 1
Product.x+= 1x de classe monteà 3totalfonctionneself.x+= 1chaque instancecrée un nouveau xx de classereste à 0mise à jourrésultataffecterrésultat
Affecter via la classe permet à tout le monde d'incrémenter le même unique compteur. Avec 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)

Construis un compteur qui incrémente une variable de classe de 1 à chaque fois qu'une instance est créée.

① Dans class Product:, ajoute une variable de classe created_count = 0.

② Dans __init__(self, name), après self.name = name, écris Product.created_count += 1 sur une seule ligne.

③ Crée Product("apple"), Product("banana") et Product("orange") à tour de rôle, puis exécute print(Product.created_count) et confirme que ça affiche 3.

Éditeur Python

Exécuter le code pour voir le résultat

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.

QUIZ

Vérification des connaissances

Répondez à chaque question une par une.

Question 1Où écris-tu une variable de classe ?

Question 2Que produit le dernier print ?
class P:
n = 10
a = P()
a.n = 99
b = 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__ ?