Python Membres De La Classe D'Initialisation
J'ai tout récemment lutté contre un bug dans Python. C'était une de ces stupides newbie de bugs, mais il m'a fait réfléchir sur les mécanismes de Python (je suis longtemps programmeur C++, Python). Je mettrai sur le code bogué et d'expliquer ce que j'ai fait pour le résoudre, et puis j'ai quelques questions...
Le scénario: j'ai une classe appelée, qui dispose d'un dictionnaire de données des membres, voici son code (c'est la simplification de cours):
class A:
dict1={}
def add_stuff_to_1(self, k, v):
self.dict1[k]=v
def print_stuff(self):
print(self.dict1)
La classe à l'aide de ce code est de la classe B:
class B:
def do_something_with_a1(self):
a_instance = A()
a_instance.print_stuff()
a_instance.add_stuff_to_1('a', 1)
a_instance.add_stuff_to_1('b', 2)
a_instance.print_stuff()
def do_something_with_a2(self):
a_instance = A()
a_instance.print_stuff()
a_instance.add_stuff_to_1('c', 1)
a_instance.add_stuff_to_1('d', 2)
a_instance.print_stuff()
def do_something_with_a3(self):
a_instance = A()
a_instance.print_stuff()
a_instance.add_stuff_to_1('e', 1)
a_instance.add_stuff_to_1('f', 2)
a_instance.print_stuff()
def __init__(self):
self.do_something_with_a1()
print("---")
self.do_something_with_a2()
print("---")
self.do_something_with_a3()
Avis que tous les appels à do_something_with_aX()
initialise une nouvelle "propre" de l'instance de la classe A, et imprime le dictionnaire avant et après l'ajout.
Le bug (dans le cas où vous n'avez pas compris encore):
>>> b_instance = B()
{}
{'a': 1, 'b': 2}
---
{'a': 1, 'b': 2}
{'a': 1, 'c': 1, 'b': 2, 'd': 2}
---
{'a': 1, 'c': 1, 'b': 2, 'd': 2}
{'a': 1, 'c': 1, 'b': 2, 'e': 1, 'd': 2, 'f': 2}
Dans la deuxième initialisation de la classe A, les dictionnaires ne sont pas vides, mais commencer avec le contenu de la dernière initialisation, et ainsi de suite. Je m'attendais à démarrer "frais".
Ce qui permet de résoudre ce "bug" est évidemment d'ajouter:
self.dict1 = {}
Dans le __init__
constructeur de la classe A. Cependant, qui m'a fait me demander:
- Quel est le sens de la "dict1 = {}" initialisation au point de dict1 de la déclaration (première ligne en classe? Il est dénuée de sens?
- Quel est le mécanisme d'instanciation qui provoque la copie de la référence à partir de la dernière initialisation?
- Si j'ajoute "l'auto.dict1 = {}" dans le constructeur (ou tout autre membre de données), comment est-il pas d'incidence sur le dictionnaire membre de déjà initialisé instances?
EDIT: Suivant les réponses que je comprends maintenant que, par la déclaration d'un membre de données et de ne pas s'y référant dans la __init__
ou quelque part d'autre que soi.dict1, je suis pratiquement la définition de ce qu'on appelle en C++/Java une donnée membre statique. En appelant à soi-même.dict1 je fais "instance" sédentaires".
- Vous devez utiliser la nouvelle-classes de style, et d'en tirer de l'objet.
- la classe A n'a pas de
__init__()
. Avez-vous intentionnellement signifie rien, ce qui est équivalent à un videdef __init__(self): pass
, d'où bien sûr il n'a pas de membres de données? Si non, veuillez corriger votre code.
Vous devez vous connecter pour publier un commentaire.
Ce que vous gardez en se référant à un bug est le documenté, le comportement standard de Python classes.
La déclaration d'une dict à l'extérieur de
__init__
que vous avez initialement n'est la déclaration d'une classe de niveau variable. Il n'est créé qu'une seule fois au début, chaque fois que vous créez de nouveaux objets, il va réutiliser cette même dict. Pour créer les variables d'instance, vous déclarez avecself
dans__init__
; son aussi simple que cela.@Matthieu : Veuillez consulter la différence entre un membre du groupe et un membre de l'objet en Programmation Orientée Objet. Ce problème se produit en raison de la déclaration de l'origine dict en fait un membre de la classe, et non pas un membre de l'objet (comme l'était l'original de l'affiche de l'intention.) Par conséquent, il existe une fois pour (est partagée à travers) toutes les instances de la classe (c'est à dire une fois pour la classe elle-même, en tant que membre de la classe de l'objet lui-même) de sorte que le comportement est parfaitement correcte.
Lorsque vous accéder à un attribut d'instance, de dire, de soi.toto, python va d'abord trouver " foo " dans
self.__dict__
. Si pas trouvé, python trouverez " foo " dansTheClass.__dict__
Dans votre cas,
dict1
est de classe A, pas l'instance.Pythons déclarations de classe sont exécutés comme un bloc de code local et de définitions de variables (dont les définitions de fonctions sont un type spécial d') sont stockées dans la construction d'instance de classe. En raison de la façon dont l'attribut de rechercher des œuvres en Python, si un attribut n'est pas trouvé sur l'instance de la valeur de la classe est utilisé.
L'est un article intéressant à propos de la classe de syntaxe sur l'histoire de Python blog.
Si c'est votre code:
Alors vous avez probablement attendu à ce que se produisent à l'intérieur de Python:
Autant que je peux dire, que la est ce qui se passe, sauf qu'un
dict
a son pointeur copié, au lieu de la valeur, ce qui est le comportement par défaut partout en Python. Regardez ce code: