Bon, alors un jours va vraiment falloir que python soit claire sur ça définition des classes et des variables parce que c'est quand même un sacré bordel.... Aller, ça fait un moment que j'ai pas divaguer sur de la prog ici, c'est parti pour un cours de classe en python ... ^^ Alors du coup, petit rappel. Python est un langage de programmation initialement centré sur le scripting (faire une série de fonctions qui vont s'exécuter à la suite l'un de l'autre dans un seul fichier et que l'on peut exécuter directement). Il est depuis devenu un langage de programmation avec entre autres des brides de programmation objet via la création de Classe.Mais qu'est ce qu'une classe du coup ?Une classe, c'est un plan de construction pour définir des objets. Par exemple, dans un jeu vidéo, un personnage va souvent être représenté par une classe Personnage qui contient tous les attributs des personnages (vie, vitesse, armure, etc...). Ainsi je pourrais créer différents personnage rapidement, par exemple: Thomas(100, 250, 1000, ...), Switchelven(50, 300, 300), MamanFreg(150, 150, 800....).... Les attributs peuvent être définis de plusieurs manière:public (utilisable && visible partout dans le code), privée (utilisable && visible uniquement dans la classe) (certains langage ont d'autre variante comme protected en JAVA mais on ne va pas rentrer dans les détails ^^), d'instance (définie comme existante pour tout représentant de la classe mais propre à chaque représentant), ou de classe (définie comme existante et partagée par tout représentant). En python, la notion de variable publique ou privée est assez claire depuis quelque version pars l'utilisation du préfix __ (la variable toto est public mais __titi est privée). Par contre, la différence entre une variable de classe et une variable d'instance est plutôt floue.... Reprenons nos personnages. Une définition en python de la classe personnage serait:class Personnage: life: int speed: int armor: int def __init__(self, life: int, speed: int, armor: int): self.life = life self.speed = speed self.armor = armor Ici, toutes les variables sont des variables d'instances. Selon la documentation de python, une variable est propre à l'instance lorsqu'elle est définie dans la méthode d'`__init__` permettant d'instancier la classe. Si elle n'est présente que dans le corps de la classe, elle devient une variable de classe.Par exemple, ajoutons à nos personnage une vie maximum, une occupation, et une liste de ville existante:class Personnage: life: int speed: int armor: intmax_life: int = 100occupation: str = 'qwicer'cities: List[str] = ['paris', 'cayeux', 'quebec', 'rouen'] def __init__(self, life: int, speed: int, armor: int): self.life = life self.speed = speed self.armor = armorCelles-ci sont désormais des variables reliées et attendues communes à tous les personnages. Et c’est là que commence la magie noire de python. En générale, nous nous attendons au comportement suivant:Si je modifie la valeur d’une variable d’instance, alors la modification n’est visible que dans l’instance en questionSi je modifie la valeur d’une variable de classe, alors la modification est partagée par toutes les instances de la classeAutrement dit, si je crée les méthodes suivantes pour ma classe personnage: def take_damage(self, attack: int): self.life -= max(attack/(self.armor/10),1) def set_max_life(self, new_value: int): self.max_life = new_value def add_city(self, new_city: str): self.cities.append(new_city)Et que je les appliques à nos personnages:thomas.take_damage(10) -> thomas doit perdre un point de vie, donc se retrouve à 99switchelven.take_damage(60) -> switchelven doit perdre 2 PV, donc se retrouve à 48maman_freg.add_city(‘Hanoï’) -> tout le monde connaît desormais Hanoïthomas = Personnage(100,250,1000)switchelven = Personnage(50,300,300)maman_freg = Personnage(150,150,800)thomas.take_damage(10)print("Thomas' life: ", thomas.life)print("Switchelven's life: ", switchelven.life)switchelven.take_damage(60)print("Thomas' life: ", thomas.life)print("Switchelven's life: ", switchelven.life)maman_freg.add_city("Hanoï")print("Thomas' cities: ", thomas.cities)print("Switchelven's cities: ", switchelven.cities)print("Maman Freg's cities: ", maman_freg.cities)→ Thomas' life: 99→Switchelven's life: 50→Thomas' life: 99→Switchelven's life: 48.0→Thomas' cities: ['paris', 'cayeux', 'quebec', 'rouen', 'Hanoï']→Switchelven's cities: ['paris', 'cayeux', 'quebec', 'rouen', 'Hanoï']→Maman Freg's cities: ['paris', 'cayeux', 'quebec', 'rouen', 'Hanoï']Jusque là tout va bien. Mais du coup, si je modifie max_life via la méthode set_max_life, tout le monde devrait avoir la nouvelle valeur, c’est bien ça ? Testons…. maman_freg.set_max_life(1000)print("Thomas' max life: ", thomas.max_life)print("Switchelven's max life: ", switchelven.max_life)print("Maman Freg's max life: ", maman_freg.max_life)→Thomas' max life: 100→Switchelven's max life: 100→Maman Freg's max life: 1000Hum… Pas tout à fait ce qu’on attendait n’est ce pas… Mais alors que se passe-t-il ? Eh bien, python étant un langage centré sur la lisibilité et n’ayant pas vraiment l’habitude d’interdire les cas incohérents à ses utilisateurs (ce qui peut vite conduire à des situations inattendues ou des listes se font remplacer par des entiers ….), il gère un peu comme il veut un cas de variable de classe incorrectement modifier. Et oui, si l’on veut proprement modifier une variable de classe via une méthode en python, il faut définir la méthode comme appartenant à la classe (logique en soit). Comme par exemple: @classmethod def set_occupation(cls, new_occupation: str): cls.occupation = new_occupationDans ce cas, occupation sera bien modifier pour toutes les instances. Mais sans cette définition …. python ce joue de nous. Si la variable est altérable (liste, dictionnaire, etc), c’est-à-dire que l’on peut y ajouter des informations sans modifier la variable en soit, alors la modification sera répercutée sur la classe quoi qu’il arrive. Si par contre, la variable est immuable (chaîne de caractère, entier, etc), alors la modification sera propre à l’instance si la méthode n’est pas une méthode de classe. Voilà voilà… Merci python pour le retournement de cerveau… Et si un jour vous pouviez uniformiser ça, ce serait quand même plus simple pour tout le monde non ?En espérant vous avoir aussi perturbé que moi quand je suis tombé dessus.Une version exécutable du code est ici: https://colab.research.google.com/drive/1m1k08H5rWGs6g52aTgtuTGtrQHgW4bBN?usp=sharingBonne soirée.PS: ça pourrait être cool un support de la syntaxe de code style MD (je dis ça, je dis rien ^ ^)