J'ai été surpris d'apprendre qu'une variable de classe d'une sous-classe ne peut pas accéder à une variable de classe du parent sans indiquer spécifiquement le nom de classe du parent:
>>> class A(object):
... x = 0
...
>>> class B(A):
... y = x+1
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in B
NameError: name 'x' is not defined
>>> class B(A):
... y = A.x + 1
...
>>> B.x
0
>>> B.y
1
Pourquoi est-ce qu'en définissant B.y je dois me référer à A.x et pas seulement à x? C'est contraire à mon intuition des variables d'instance, et puisque je peux me référer à B.x après que B soit défini.
En Python, le corps d'une classe est exécuté dans son propre espace de noms avant la création de la classe (après quoi, les membres de cet espace de noms deviennent les membres de la classe). Ainsi, lorsque l'interpréteur atteint y = x + 1, la classe B n'existe pas encore à ce stade et, par conséquent, n'a pas de parent.
Pour plus de détails, voir http://docs.python.org/reference/compound_stmts.html#class-definitions
Les règles de portée de Python pour les noms de barres sont très simples et directes: l'espace de noms local d'abord, puis (le cas échéant) les fonctions externes dans lesquelles l'implémentation actuelle est imbriquée, puis les globales, enfin les fonctions intégrées. C'est tout ce qui se produit lorsqu'un nom de barre est recherché, et il n'est pas nécessaire de mémoriser ou d'appliquer des règles compliquées (ni de compilateur Python pour appliquer des règles plus compliquées).
Chaque fois que vous voulez une recherche différente, vous utiliserez un nom qualifié, pas un nom n. Les noms qualifiés sont beaucoup plus puissants car la recherche peut toujours être déléguée aux objets dont les attributs peuvent être demandés, et ces objets peuvent implémenter les règles de recherche dont ils ont besoin. En particulier, dans une méthode d'instance au sein d'une classe, self.x
est la manière de demander à l'objet self
de rechercher le nom d'attribut 'x'
- et dans cette recherche, il peut déléguer aux classes, y compris l'implémentation du concept d'héritage (et l'héritage multiple, l'ordre de résolution des méthodes, etc.).
Le corps d'une classe (par opposition aux corps des méthodes définies dans une classe) s'exécute dans le cadre de l'instruction class
, avant l'objet de classe est créé ou son nom est lié (en particulier, avant qu'une des bases ait été définie comme étant des bases - bien que ce dernier détail ne puisse jamais avoir d'importance quand on se réfère aux noms de barres, de toute façon! - ).
Donc, dans votre exemple, dans la classe B
, le nom de barre x
est recherché avec les règles universelles - est-ce un nom lié localement? Si non, est-il lié à une fonction externe dans laquelle cette portée est imbriquée? Si non, est-il lié en tant que global ou intégré? Si rien de ce qui précède, l'utilisation du nom de barre en question entraîne bien sûr une exception d'erreur de nom.
Puisque vous voulez une séquence de recherche différente de celle que les règles de recherche de nom de barre appliquent universellement, alors vous devez clairement utiliser un nom qualifié, pas un nom de barre; et un instant de réflexion montrera clairement que le "seul choix évident" pour un nom qualifié à utiliser dans votre but doit être A.x
- puisque c'est là que vous voulez qu'il soit recherché (les bases n'ont été enregistrées nulle part pourtant à ce moment-là, après tout ... ce sera la métaclasse, normalement type
, qui fera la liaison de bases dans le cadre de son travail lorsqu'elle sera appelée après le corps de classe a terminé son exécution! -).
Certaines personnes sont si profondément attachées à d'autres règles "magiques" pour la recherche de noms de barres qu'elles ne peuvent tout simplement pas supporter cet aspect de Python (inspiré à l'origine, je crois, par Modula-3, un langage peu connu qui est très bien considéré dans les cercles des théoriciens ;-) - avoir à écrire self.x
dans une méthode pour spécifier que x
doit être recherché sur self
plutôt que d'utiliser les règles de nom de barre universelles, par exemple, rend ces gens fous.
Moi, j'aime la simplicité et l'universalité des règles de recherche de noms de barres, et j'aime utiliser des noms qualifiés au lieu de noms de barres chaque fois que je veux n'importe quelle autre forme de recherche ... mais alors, ce n'est pas un secret que je suis follement amoureux de Python (j'ai mes propres grognements - par exemple, global x
comme une déclaration fait toujours ramper ma peau, où je préfère de loin écrire global.x
, c'est-à-dire que global
soit un nom intégré pour "le module en cours d'exécution" ... I do j'adore les noms qualifiés! -), n'est-ce pas? -)