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

Un même identifiant pour des objets différents ?

21 réponses
Avatar
Francois
Bonjour à tous,

pourrais-je avoir s'il vous plaît une explication sur cet exemple ? Deux
objets sont bel et bien différents, mais ont le même identifiant ?

Merci d'avance.

#----------------------------------------
>>> class MaClasse(object):
... def (self): print "bonjour"
... def g(self): print "Au revoir"
...
>>> MaClasse.f is MaClasse.g #les deux objets sont différents,
False
>>> id(MaClasse.f) == id(MaClasse.g) #mais ont le même id ?
True
#----------------------------------------


--
François

10 réponses

1 2 3
Avatar
Francois
Francois a écrit :
#----------------------------------------
>>> class MaClasse(object):
... def (self): print "bonjour"
... def g(self): print "Au revoir"
...
>>> MaClasse.f is MaClasse.g #les deux objets sont différents,
False
>>> id(MaClasse.f) == id(MaClasse.g) #mais ont le même id ?
True
#----------------------------------------



Pardon, à la ligne 2, il faut lire « def f(self): print "bonjour" ».


--
François
Avatar
Méta-MCI \(MVP\)
Bonsoir !

Je viens de vérifier. Et, j'ai bien la même chose.
Et, avoir le même id, cela me semble curieux...

J'ai instancié, et c'est pareil :
mc=MaClasse()
print id(mc.f), id(mc.g)


La doc dit :

id( object)
Return the ``identity'' of an object. This is an integer (or long
integer) which is guaranteed to be unique and constant for this object
during its lifetime. Two objects with non-overlapping lifetimes may have
the same id() value. (Implementation note: this is the address of the
object.)


Mais le mot "overlapping" n'est pas, selon moi, d'un français assez
correct pour que je le comprenne...


@+

MCI
Avatar
Francois
Méta-MCI (MVP) a écrit :
La doc dit :

id( object)
Return the ``identity'' of an object. This is an integer (or long
integer) which is guaranteed to be unique and constant for this object
during its lifetime. Two objects with non-overlapping lifetimes may have
the same id() value. (Implementation note: this is the address of the
object.)


Mais le mot "overlapping" n'est pas, selon moi, d'un français assez
correct pour que je le comprenne...



Pour moi, la dernière phrase signifie : « Deux objets dont les durées de
vie ne se chevauchent pas peuvent éventuellement avoir la même valeur
id(). »

Donc, pour que les objets MaClasse.f et MaClasse.g aient la même valeur
id() (alors que se sont des objets différents ça c'est sûr -- enfin je
pense...), la seule explication est qu'ils ont tous les deux des durées
de vie qui ne se chevauchent pas dans le temps, ce qui est bizarre...








--
François
Avatar
Francois
Tant que j'y suis, il y a aussi ceci que je ne comprends pas :

#-----------------------------------
>>> class Mc(object):
... def f(self): print "Bonjour"
...
>>> Mc.f is Mc.f
False
>>> id(Mc.f) == id(Mc.f)
True
#-----------------------------------

Cette fois-ci, c'est l'inverse que je ne comprends pas. Pourquoi « Mc.f
is Mc.f » retourne False ?

--
François
Avatar
Bruno Desthuilliers
Francois a écrit :
Bonjour à tous,

pourrais-je avoir s'il vous plaît une explication sur cet exemple ?
Deux objets sont bel et bien différents, mais ont le même identifiant
?

Merci d'avance.

#----------------------------------------
class MaClasse(object):






... def f(self): print "bonjour" ...


> def g(self): print "Au revoir" ...
MaClasse.f is MaClasse.g #les deux objets sont différents,






False
id(MaClasse.f) == id(MaClasse.g) #mais ont le même id ?






True #----------------------------------------



Tant que j'y suis, il y a aussi ceci que je ne comprends pas :

#-----------------------------------
class Mc(object):






... def f(self): print "Bonjour" ...
Mc.f is Mc.f






False
id(Mc.f) == id(Mc.f)






True #-----------------------------------

Cette fois-ci, c'est l'inverse que je ne comprends pas. Pourquoi «
Mc.f is Mc.f » retourne False ?




Les fonctions implémentent le protocole descripteur (le même qui permet
les properties) de façon à retourner un objet method quand elles sont
utilisées comme attributs. Un nouvel objet method est instancié à chaque
accès. Il est donc normal que deux accès successifs à l'attribut
retournent deux objets method différents, ce que révèle le test
d'identité. Dans le cas du test d'égalité sur les identifiants, le
résultat "surprenant" vient du fait qu'au moment du second appel à id(),
l'objet method passé en argument lors du premier appel a déjà été
collecté (plus de référence dessus), donc l'espace mémoire est réutilisé
pour le second objet method (ce qui n'est pas le cas lors du test
d'identité qui garde les deux objets "vivants" le temps de son évaluation).

Si tu gardes une référence sur un des deux objets method, tu n'aura pas
le même résultat:

class Foo(object):
def bar(self): pass
barmethod = Foo.bar
id(barmethod) == id(Foo.bar)
=> False
Avatar
Eric Brunel
On Tue, 19 Aug 2008 03:59:11 +0200, Francois wrote:

Tant que j'y suis, il y a aussi ceci que je ne comprends pas :

#-----------------------------------
>>> class Mc(object):
... def f(self): print "Bonjour"
...
>>> Mc.f is Mc.f
False
>>> id(Mc.f) == id(Mc.f)
True
#-----------------------------------

Cette fois-ci, c'est l'inverse que je ne comprends pas. Pourquoi « Mc.f
is Mc.f » retourne False ?



Vus les symptomes, je dirais que Mc.f n'est pas un "vrai" attribut, mais
construit quelque-chose. Du coup:
Mc.f is Mc.f
Ce qui est renvoyé par Mc.f est construit 2 fois, et donc renvoie 2 objets
différents.

Par contre:
id(Mc.f) == id(Mc.f)
Mc.f est construit une première fois pour la partie gauche; une fois son
id renvoyé, il ne sert plus à rien => poubelle.
Mc.f est construit une seconde fois pour la partie droite. Vu que le
premier n'existe plus, son id est réutilisé (c'est un hasard, mais ça
arrive souvent), et donc les deux id se retrouvent égaux.

Dans le cas d'origine:
#----------------------------------------
class MaClasse(object):






... def f(self): print "bonjour"
... def g(self): print "Au revoir"
...
MaClasse.f is MaClasse.g #les deux objets sont différents,






False
id(MaClasse.f) == id(MaClasse.g) #mais ont le même id ?






True
#----------------------------------------
C'est en fait exactement pareil: dans l'expression 'MaClasse.f is
MaClasse.g', MaClasse.f et MaClasse.g construisent ce qu'ils doivent
construire et sont toujours présents au moment du test d'identité => ils
sont différents.
Pour le second, le simple fait d'appeler id dessus fait qu'ils sont
libérés dès qu'id renvoie sa valeur, donc l'id est réutilisé.

Juste pour voir, voilà ce que ça donne en passant par des variables
intermédiaires, afin d'éviter l'éventuelle réutilisation d'id:
----------------------------------------
class MaClasse(object):






... def f(self): pass
... def g(self): pass
...
x, y = MaClasse.f, MaClasse.g
x is y






False
x == y






False
id(x) == id(y)






False
z, t = MaClasse.f, MaClasse.f
z is t






False
z == t






True
id(z) == id(t)






False
----------------------------------------

Conclusion: ne pas s'amuser avec les 'is' et les 'id', sauf dans les cas
où le comportement est documenté (genre, 'is None').

Pour ce qui est effectivement construit par MaClasse.f, je laisse ça aux
spécialiste(s) (au hasard, Bruno?).

HTH
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"
Avatar
Bruno Desthuilliers
Eric Brunel a écrit :
(snip)
Pour ce qui est effectivement construit par MaClasse.f, je laisse ça aux
spécialiste(s) (au hasard, Bruno?).



<hs>
Merci pour le "spécialiste", mais je ne suis pas sûr d'en être digne !-)
</hs>


En résumé :

import types

class function(object):
# descriptor protocol, invoked when
# a function is resolved as a class attribute
def __get__(self, instance, cls):
return types.MethodType(self, instance, cls)



Et c'est ainsi que les fonctions deviennent des méthodes...
Avatar
Francois
Bruno Desthuilliers a écrit :

Les fonctions implémentent le protocole descripteur (le même qui permet
les properties) de façon à retourner un objet method quand elles sont
utilisées comme attributs. Un nouvel objet method est instancié à chaque
accès. Il est donc normal que deux accès successifs à l'attribut
retournent deux objets method différents, ce que révèle le test
d'identité.



Ok, c'est parfaitement clair. J'ai bien deux objets distincts, même si
les deux objets se ressemblent beaucoup, voire sont rigoureusement
"identiques" (à prendre au sens du langage courant). On pouvait
s'attendre quand même à ce que Python ne prenne pas la peine de
dupliquer deux fois le même objet. Mais bon, c'est l'implémentation du
protocole descripteur (avec la méthode __get__) pour les fonctions qui
veut ça si j'ai bien compris.


Dans le cas du test d'égalité sur les identifiants, le
résultat "surprenant" vient du fait qu'au moment du second appel à id(),
l'objet method passé en argument lors du premier appel a déjà été
collecté (plus de référence dessus), donc l'espace mémoire est réutilisé
pour le second objet method (ce qui n'est pas le cas lors du test
d'identité qui garde les deux objets "vivants" le temps de son évaluation).



Ok, c'est donc ce je pensais (mais sans bien le comprendre) : les deux
objets ont des durées de vie qui ne se chevauchent pas. À partir de là,
tout est possible, les valeurs de id() peuvent parfaitement être égales.

Si tu gardes une référence sur un des deux objets method, tu n'aura pas
le même résultat:

class Foo(object):
def bar(self): pass
barmethod = Foo.bar
id(barmethod) == id(Foo.bar)
=> False



Ok, c'est très clair. Merci beaucoup pour la réponse.


--
François
Avatar
Francois
Eric Brunel a écrit :

Vus les symptomes, je dirais que Mc.f n'est pas un "vrai" attribut, mais
construit quelque-chose. Du coup:
Mc.f is Mc.f
Ce qui est renvoyé par Mc.f est construit 2 fois, et donc renvoie 2
objets différents.



Exactement. Juste une chose. Je dirais plutôt que f est un "vrai"
attribut de la classe Mc, sauf que c'est un descripteur et du coup Mc.f
est intercepté par la méthode __get__ qui fait des choses... Quoi, c'est
encore un peu (très) flou pour moi, mais manifestement, ça créé un
nouvel objet à chaque fois comme vous le dîtes (ainsi que Bruno).

Par contre:
id(Mc.f) == id(Mc.f)
Mc.f est construit une première fois pour la partie gauche; une fois son
id renvoyé, il ne sert plus à rien => poubelle.
Mc.f est construit une seconde fois pour la partie droite. Vu que le
premier n'existe plus, son id est réutilisé (c'est un hasard, mais ça
arrive souvent), et donc les deux id se retrouvent égaux.



>
Dans le cas d'origine:

... [couic] ...

C'est en fait exactement pareil: dans l'expression 'MaClasse.f is
MaClasse.g', MaClasse.f et MaClasse.g construisent ce qu'ils doivent
construire et sont toujours présents au moment du test d'identité => ils
sont différents.
Pour le second, le simple fait d'appeler id dessus fait qu'ils sont
libérés dès qu'id renvoie sa valeur, donc l'id est réutilisé.



D'accord aussi. C'est ce que disais Bruno aussi.

Juste pour voir, voilà ce que ça donne en passant par des variables
intermédiaires, afin d'éviter l'éventuelle réutilisation d'id:



Et oui, dès qu'on garde en vie les objets en les liant à des noms de
variables, c'est une autre histoire. Bruno avait aussi donné un exemple.

Conclusion: ne pas s'amuser avec les 'is' et les 'id', sauf dans les cas
où le comportement est documenté (genre, 'is None').



On peut dire ça en effet. Remarquez, je dirais que 'is' est franc du
collier en fait et que c'est 'id' qui est fourbe. Car avec 'is', je
pense qu'on peut être sûr que les deux objets sont vivants au même
moment et qu'on obtient True si et seulement si c'est le *même* objet
(dans le sens où les deux objets ne sont en réalité qu'un seul et même
objet, c'est-à-dire les deux objets sont stockés physiquement exactement
dans la même zone mémoire de l'ordinateur). Avec id(), on est pas sûr
que les objets existent au même moment ce qui peut induire des résultats
inattendus. J'ai l'impression qu'à partir du moment où l'on lie deux
objets à des noms de variables, là en revanche, on est sûr que 'id' et
'is' sont strictement équivalents.

Pour ce qui est effectivement construit par MaClasse.f, je laisse ça aux
spécialiste(s) (au hasard, Bruno?).



C'est une question sur laquelle je me pencherai. Comme l'a dit Bruno,
les descripteurs sont la base de l'orienté objet en Python. Pour
l'instant, je comprends qu'avec obj = MaClasse(), obj.f construit un
truc qui fait que f est liée à obj et que donc lorsqu'on applique la
méthode f à obj (obj.f()), on a pas besoin de préciser la valeur du
paramètre self (obj en l'occurence) dans f.

HTH



Oui, merci pour la réponse.


--
François
Avatar
Francois
Je viens de constater que la question a été évoquée sur comp.lang.python
ici :

http://groups.google.com/group/comp.lang.python/browse_thread/thread/635a880be62d7205/304ee9468bc5ee0e

Les mêmes choses sont dîtes il me semble. Par contre, j'ai un petit
problème de traduction. Dans le troisième message, il y a ceci :

« The returned object is a wrapper created on-the-fly as needed. You've
requested two of them and each wrapper has a different object id but
wraps an identical source. »

Comment traduiriez vous le mot "wrapper" dans ces phrases ?

--
François
1 2 3