Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

question de programmation objet

1 réponse
Avatar
laurent FRANCOIS
Bonjour,

Voila j'ai beaucoup de mal à m'y retrouver dans les espaces de
nommage. Un objet appartient à quel espace de nommage?
Comment faire pour le mettre dans un espace de nommage donné?
Comment faire pour accéder à un objet quant on n'arrive pas à
comprendre dans quel espace de nommage il est?
Quand je lis du code j'ai la plus part du temps à voir dans
quel espace de nommage appartient tel ou tel objet. (sauf si c'est archi
basique ie que des objets global)

Par exemple:

class Foo(object):
... colLabels = ["Forst", "Last"]
... def GetColLabelValue(self, col):
... return self.colLabels[col]

colLabels est un attribut de classe (C'est ça?). et je découvre que
je peux y accéder par self.colLabels (C'est un attribut de l'instance
de Foo?). Je suis perplexe. Je n'arrive même pas à expliquer ce que je
ne comprends pas.

Si encore on avait codé:

class Foo(object):
... def __init__(self):
... self.colLabels = ["Forst", "Last"]
... def GetColLabelValue(self, col):
... return self.colLabels[col]

Là pas de probleme. Je crois avoir compris. Lors de l'instanciation
__init__ crée un attribut d'instance (colLabels) qui "pointe" vers la
liste ["Forst", "Last"]

Et si je code:

class Foo(object):
... colLabels = ["Forst", "Last"]
... def GetColLabelValue(self, col):
... return colLabels[col]
...
>>> foo = Foo()
>>> foo.GetColLabelValue(1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 4, in GetColLabelValue
NameError: global name 'colLabels' is not defined


Là je suis encore plus perplexe. J'ai l'impression
qu'il y a quelque chose de basique que je n'ai pas
compris dans la POO. Est-ce qu'il y en a parmi vous
qui voient quel est mon probleme?

Merci

1 réponse

Avatar
Bruno Desthuilliers
laurent FRANCOIS a écrit :
Bonjour,

Voila j'ai beaucoup de mal à m'y retrouver dans les espaces de
nommage. Un objet appartient à quel espace de nommage?



Si tu poses la question en ces termes, tu risques effectivement d'avoir
du mal à t'y retrouver !-)

Un objet "n'appartient" pas à un espace de nommage - ce sont les _noms_
qui appartiennent à un espace de nommage. En Python, une "variable"
n'est pas une étiquette sur un espace mémoire, c'est l'association d'un
nom et d'une *référence* sur un objet. Plusieurs noms, qu'ils
appartiennent au même espace de nommage ou non, peuvent référencer
simultanément le même objet.

En fait, le plus simple est de considérer les espaces de nommage comme
des dicts nom=>référence sur un objet. Et c'est d'ailleurs souvent ce
qu'ils sont effectivement.

Comment faire pour le mettre dans un espace de nommage donné?



cf ci-dessus. Tout ce que tu peux "mettre" dans un espace de nommage,
c'est un nom.

Comment faire pour accéder à un objet quant on n'arrive pas à
comprendre dans quel espace de nommage il est?



Là c'est autre chose.

Quand je lis du code j'ai la plus part du temps à voir dans
quel espace de nommage appartient tel ou tel objet. (sauf si c'est archi
basique ie que des objets global)

Par exemple:

class Foo(object):
... colLabels = ["Forst", "Last"]
... def GetColLabelValue(self, col):
... return self.colLabels[col]

colLabels est un attribut de classe (C'est ça?).



Oui. En fait, les classes étant elles-même des objets (instances de leur
métaclasse, par défaut 'type'), c'est un attribut de l'objet "Foo". Si
tu inspectes Foo.__dict__, tu y trouvera "colLabels" et "GetColLabelValue".

et je découvre que
je peux y accéder par self.colLabels (C'est un attribut de l'instance
de Foo?)



Non. Voir ci-dessous.

. Je suis perplexe. Je n'arrive même pas à expliquer ce que je
ne comprends pas.



C'est souvent le cas quand on ne comprends pas !-)

La règle de résolution d'attributs - telle qu'implémentée par
object.__getattribute__ - est la suivante:

1/ regarder si le nom existe dans le __dict__ de la classe ou d'une des
classes parents. Si oui ET que l'attribut correspondant est un
descripteur, retourner le résultat de l'appel à sa méthode __get__.

2/ Sinon, regarder si le nom existe dans le __dict__ (ou les __slots__)
de l'instance. Si oui, retourner l'attribut correspondant.

3/ Sinon, regarder si la classe (ou une des classes parent) définit la
methode __getattr__. Si oui, retourner le résultat de l'appel à __getattr__

4/ Sinon, regarder si le nom existe dans le __dict__ de la classe ou
d'une des classes parents. Si oui, retourner l'attribut correspondant

5/ si toutes les méthodes précédentes ont échouées, lever un AttributeError


Et si je code:

class Foo(object):
... colLabels = ["Forst", "Last"]
... def GetColLabelValue(self, col):
... return colLabels[col]



Tu a un NameError.

...
>>> foo = Foo()
>>> foo.GetColLabelValue(1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 4, in GetColLabelValue
NameError: global name 'colLabels' is not defined



Bingo.


Là je suis encore plus perplexe. J'ai l'impression
qu'il y a quelque chose de basique que je n'ai pas
compris dans la POO.



Ce n'est pas un problème de POO, c'est un problème de portée et de
modèle d'exécution.

En Python, à peu près tout se passe à l'exécution. Entre autres,
l'instruction "class" et l'instruction "def" sont des instructions
exécutables. Lors de l'exécution de "class", un espace de nommage
temporaire (correspondant au bloc 'class') est créé, qui contient tous
les noms définis dans ce bloc ET à ce niveau là - soit par une
assignation, soit par un "def". Cet espace de nommage (un dict en fait)
est ensuite passé - avec le nom de la classe et la liste des classes
parentes - à la méthode __new__ de la métaclasse, laquelle méthode se
charge de retourner le nouvel objet class, dûment initialisé avec les
noms en question comme attributs. C'est ainsi que, dans ton example,
colLabels (un objet liste) et GetColLabelValue (un objet function)
deviennent des attributs de la classe Foo.

Par contre, les noms préalablement définis dans cet espace de nommage ne
sont pas "capturés" par les fonctions définies dans ce même espace. Donc
quand GetColLabelValue est appelée, c'est la règle normale qui
s'applique pour résoudre "colLabels" - à savoir l'espace de nommage
local, puis l'espace de nommage "global" (c'est à dire celui du
module)[1]. Comme le nom en question n'est défini dans aucun de ses deux
espaces de nommages, tu a un NameError.

Il faut bien comprendre que pour une fonction, le fait d'être définie
dans un bloc "class" ne change rien en soi. Tu a exactement le même
fonctionnement en définissant ta fonction en dehors de la classe et en
l'assignant à la classe ensuite. Ce qui "transforme" la fonction en
"méthode" (une méthode n'étant rien d'autre qu'un objet appelable avac
comme attributs la fonction, la classe et l'instance) est simplement une
application du protocole descripteur (et c'est d'ailleurs une des
raisons d'être de ce protocole - fournir un mécanisme générique pour les
attributs calculés).

Accessoirement, tout ce mode de fonctionnement est propre au modèle
objet de Python, qui est très particulier.

[1] je passe sur le cas des fermetures, puisque ta fonction n'en est pas
une.


HTH