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

Clonage

17 réponses
Avatar
Méta-MCI \(MVP\)
Bonjour !

Je ne dois pas être bien réveillé, car je ne comprend pas pourquoi ce
code :

x=123
y=copy.deepcopy(x)
z=copy.copy(x)
print "x",id(x)
print "y",id(y)
print "z",id(z)

me donne toujours le même identifiant, pour les objets x, y et z ?
Et, comment cloner un objet ? (une fonction, par exemple). Dois-je
systématiquement passer par new ?

@+

Michel Claveau

10 réponses

1 2
Avatar
Pierre Maurette
Bonjour !

Je ne dois pas être bien réveillé, car je ne comprend pas pourquoi ce code :

x3
y=copy.deepcopy(x)
z=copy.copy(x)
print "x",id(x)
print "y",id(y)
print "z",id(z)

me donne toujours le même identifiant, pour les objets x, y et z ?
Et, comment cloner un objet ? (une fonction, par exemple). Dois-je
systématiquement passer par new ?


Je colle mon wagonnet au train - en tout bien tout honneur - de vos
questions. Je pense d'ailleurs que vous trouverez la réponse avant moi.
Je ne suis pas trop étonné du résultat de vos tests. Mais j'arrive, à
mon niveau, à un défaut de cohérence, effectivement.
J'ai regardé le manuel, doc du module copy, et ce lien:
<URL:http://www.ibm.com/developerworks/opensource/library/os-python1/>
- On lit que la différence entre copy.copy et copy.deepcopy n'a de sens
que pour des types composés.
- Bien que n'ayant pas trouvé la définition définitive de id(), je
crois comprendre que les types simples de même valeur renvoient toujour
la même id().
- Je ne vois pas trop comment __new__ peut changer les choses.
- Je ne suis pas très convaincu par la générélisation "tout est objet"
en Python.
- Le comportement devient celui qu'on attend quand on encapsule un int
dans un "vrai" objet minimal. On aurait alors un objet au sens attendu
contenant une référence (sic) à un int.
- Le comportement de int(123) serait dans mon idée comparable à
"pierre" dans :
const char* chaine1 = "pierre";
/* ... */
const char* chaine2 = "pierre";

(le const n'est pas "obligatoire").

class MyInt (object):
def __init__(self, value):
self.i = value



def main():
import sys, copy
x=MyInt(123)
y=copy.deepcopy(x)
z=copy.copy(x)
t=MyInt(1234)
u=MyInt(123)
v=x
print "x",id(x),x.i
print "v",id(v),v.i
print "y",id(y),y.i
print "z",id(z),z.i
print "t",id(t),t.i
print "u",id(u),u.i
print "----------------"
x=int(123)
lx=long(123)
y=copy.deepcopy(x)
z=copy.copy(x)
t=int(1234)
u=int(123)
v=x
w = int.__new__(int, 123)
ww = int.__new__(int, x)
print "x",id(x),x
print "w",id(w),w
print "ww",id(ww),ww
print "lx",id(lx),lx
print "v",id(v),v
print "y",id(y),y
print "z",id(z),z
print "t",id(t),t
print "u",id(u),u
print "----------------"
x=[123]
lx=[long(123)]
y=copy.deepcopy(x)
z=copy.copy(x)
t=[1234]
u=[123]
v=x
print "x",id(x),x
print "lx",id(lx),lx
print "v",id(v),v
print "y",id(y),y
print "z",id(z),z
print "t",id(t),t
print "u",id(u),u

if __name__ == '__main__':
main()

--
Pierre Maurette

Avatar
bruno.desthuilliers
On 8 avr, 10:55, "Méta-MCI (MVP)"
wrote:
Bonjour !

Je ne dois pas être bien réveillé, car je ne comprend pas pourquoi c e
code :

x3
y=copy.deepcopy(x)
z=copy.copy(x)
print "x",id(x)
print "y",id(y)
print "z",id(z)

me donne toujours le même identifiant, pour les objets x, y et z ?


Faire une copy (shallow ou deep) d'un objet immutable est une drôle
d'idée...

Mais bon, pour répondre à ta question, CPython utilise un cache pour
les petits entiers (pour une définition de petit qui varie selon les
versions) et certaines chaines de caractères (celle qui respectent la
syntaxe d'un identifiant Python IIRC). C'est en tout état de cause un
détail d'implémentation.

Et, comment cloner un objet ? (une fonction, par exemple). Dois-je
systématiquement passer par new ?


Normalement, copy.deepcopy devrait faire l'affaire. Peut-être devrait
tu tester sur un objet mutable ?-)

Avatar
bruno.desthuilliers
On 8 avr, 13:07, Pierre Maurette wrote:

Bonjour !

Je ne dois pas être bien réveillé, car je ne comprend pas pourquoi ce code :

x3
y=copy.deepcopy(x)
z=copy.copy(x)
print "x",id(x)
print "y",id(y)
print "z",id(z)

me donne toujours le même identifiant, pour les objets x, y et z ?
Et, comment cloner un objet ? (une fonction, par exemple). Dois-je
systématiquement passer par new ?


Je colle mon wagonnet au train - en tout bien tout honneur - de vos
questions. Je pense d'ailleurs que vous trouverez la réponse avant moi.
Je ne suis pas trop étonné du résultat de vos tests. Mais j'arrive, à
mon niveau, à un défaut de cohérence, effectivement.
J'ai regardé le manuel, doc du module copy, et ce lien:
<URL:http://www.ibm.com/developerworks/opensource/library/os-python1/>
- On lit que la différence entre copy.copy et copy.deepcopy n'a de sens
que pour des types composés.
- Bien que n'ayant pas trouvé la définition définitive de id(),


id(obj) renvoie l'identifiant unique d'un objet - pour une définition
qui dépend de l'implémentation (en CPython, c'est l'adresse mémoire,
tout simplement).

id(obj1) == id(obj2) <=> obj1 is obj2

je
crois comprendre que les types simples de même valeur renvoient toujour
la même id().


Il n'y a pas de "types simples" en Python - tout est objet. Par
contre, certains types - notamment les entier, les floats et les
chaines - sont immutables. Une implémentation peut donc sans risque
utiliser un cache pour des instances de ces types, cf ma réponse à MC
pour ce qu'il en est avec CPython.

- Je ne vois pas trop comment __new__ peut changer les choses.
- Je ne suis pas très convaincu par la générélisation "tout est ob jet"
en Python.


Ah bon ? Trouve moi une seule chose qui puisse être à droite d'une
assignation et qui ne soit pas un objet...


Avatar
Pierre Maurette
On 8 avr, 13:07, Pierre Maurette wrote:

Bonjour !

Je ne dois pas être bien réveillé, car je ne comprend pas pourquoi ce code
: x3
y=copy.deepcopy(x)
z=copy.copy(x)
print "x",id(x)
print "y",id(y)
print "z",id(z)

me donne toujours le même identifiant, pour les objets x, y et z ?
Et, comment cloner un objet ? (une fonction, par exemple). Dois-je
systématiquement passer par new ?


Je colle mon wagonnet au train - en tout bien tout honneur - de vos
questions. Je pense d'ailleurs que vous trouverez la réponse avant moi.
Je ne suis pas trop étonné du résultat de vos tests. Mais j'arrive, à
mon niveau, à un défaut de cohérence, effectivement.
J'ai regardé le manuel, doc du module copy, et ce lien:
<URL:http://www.ibm.com/developerworks/opensource/library/os-python1/>
- On lit que la différence entre copy.copy et copy.deepcopy n'a de sens
que pour des types composés.
- Bien que n'ayant pas trouvé la définition définitive de id(),


id(obj) renvoie l'identifiant unique d'un objet - pour une définition
qui dépend de l'implémentation (en CPython, c'est l'adresse mémoire,
tout simplement).

id(obj1) == id(obj2) <=> obj1 is obj2


Oui, je pressentais l'idée d'adresse. Ou lvalue (avec 'l' lu comme
'location').


je
crois comprendre que les types simples de même valeur renvoient toujour
la même id().


Il n'y a pas de "types simples" en Python - tout est objet. Par
contre, certains types - notamment les entier, les floats et les
chaines - sont immutables.


J'avais lu "The simple types" dans
<URL:http://www.ibm.com/developerworks/opensource/library/os-python1/>
communiqué dans ma réponse à MC. Et le fait qu'ils étaient des
"instances immutables" (c'est moi qui ajoute "instances").

Une implémentation peut donc sans risque
utiliser un cache pour des instances de ces types, cf ma réponse à MC
pour ce qu'il en est avec CPython.


J'ai lu avec intérêt ;-)

- Je ne vois pas trop comment __new__ peut changer les choses.
- Je ne suis pas très convaincu par la générélisation "tout est objet"
en Python.


Ah bon ? Trouve moi une seule chose qui puisse être à droite d'une
assignation et qui ne soit pas un objet...


Oui.
Ceci dit, j'envoie rarement des débats de fond, et quand j'écris "Je ne
suis pas convaincu", ça signifie "Je ne suis pas encore convaincu", ou
mieux, "Je n'ai pas encore tout à fait pigé".


--
Pierre Maurette



Avatar
Méta-MCI \(MVP\)
Re !

Normalement, copy.deepcopy devrait faire l'affaire. Peut-être devrait
tu tester sur un objet mutable ?-)


ça m'apprendra à trop vouloir simplifier, avant d'envoyer sur le
newsgroup...

En fait, j'avais le problème avec le code suivant :


import copy

def deco(f):
print "Fonction ",f.__name__
return f

@deco
def prof(a,b,c):
this=prof
this.a=a
this.b=b
this.c=c
return a+b+c

print prof(1,2,3),id(prof)
b=copy.deepcopy(prof)
print b(111,222,333),id(b)



Ce qui m'angoisse, c'est que id(prof)=id(b) ; et donc, que deepcopy ne
clone pas...

J'ai aussi quelques déboires avec les decorators, qui sont exécutés à la
compilation, et ne sont pas dynamiques ; mais c'est une autre histoire.

@+
--
Michel Claveau



PS : je sais parfaitement que "this=prof" affecte prof, et non la
fonction courante. C'est juste du code en cours.

Avatar
bruno.desthuilliers
On 8 avr, 16:51, "Méta-MCI (MVP)"
wrote:
Re !

Normalement, copy.deepcopy devrait faire l'affaire. Peut-être devrait
tu tester sur un objet mutable ?-)


ça m'apprendra à trop vouloir simplifier, avant d'envoyer sur le
newsgroup...

En fait, j'avais le problème avec le code suivant :

import copy

def deco(f):
print "Fonction ",f.__name__
return f

@deco
def prof(a,b,c):
this=prof
this.a=a
this.b=b
this.c=c
return a+b+c

print prof(1,2,3),id(prof)
b=copy.deepcopy(prof)
print b(111,222,333),id(b)

Ce qui m'angoisse, c'est que id(prof)=id(b) ; et donc, que deepcopy ne
clone pas...


Après test simplifié sur une fonction non décorée, il semble
effectivement que deepcopy ne clone pas *les fonctions*. Ce qui est
d'ailleurs mentionné en toutes lettres dans le FameuxManuel(tm):

"""
This version does not copy types like module, class, function, method,
nor stack trace, stack frame, nor file, socket, window, nor array,
nor
any similar types.
"""

Ceci étant, définir ton propre type appelable n'est pas spécialement
du niveau de la science aérospatiale... Le seul point sensible est de
penser à implémenter correctement le protocol descripteur si tu veux
pouvoir utiliser une instance de ton type appelable comme méthode.

Peux-tu nous en dire plus sur les raisons qui t'amènent à vouloir
cloner une fonction ?

J'ai aussi quelques déboires avec les decorators, qui sont exécutés à la
compilation,


Pas à ma connaissance, non. Un décorateur n'est jamais qu'un objet
appelable retourant un objet appelable - bref une fonction d'ordre
supérieur, et tout ce que fait le compilateur à ma connaissance est de
transformer - au point du vue du byte-code généré, of course - ceci

@deco
def func(): pass

en cela

def func(): pass
func = deco(func)

les deux étant strictement interchangeables et équivalents dans un
code source.


Avatar
Francois
Il n'y a pas de "types simples" en Python - tout est objet. Par
contre, certains types - notamment les entier, les floats et les
chaines - sont immutables.


Pouvez vous me dire ce que vous voulez dire par "immutables" ? Qu'on ne
peut pas modifier ? C'est ce à quoi j'ai pensé au départ. Mais j'ai
l'impression que ce n'est pas ça car les variables de type entiers ou
float sont modifiables.


Une implémentation peut donc sans risque
utiliser un cache pour des instances de ces types, cf ma réponse à MC
pour ce qu'il en est avec CPython.


Je ne vais pas tarder à poster un fil sur ce genre de considérations,
mais je profite de cette remarque pour poser la question suivante :

id() donne un identifiant d'une variable. Un même id() pour deux
variable signifie que le contenu des variable est le même objet physique
dans la mémoire (si j'ai bien compris). Chez moi, id() ne donne pas une
adresse mémoire (il me semble en tout cas car j'ai un truc du genre
"3079857356L"). Existe-t-il une instruction pour avoir l'adresse mémoire
d'une variable ?


--
François

Avatar
Amaury Forgeot d'Arc
Bonsoir,

Il n'y a pas de "types simples" en Python - tout est objet. Par
contre, certains types - notamment les entier, les floats et les
chaines - sont immutables.


Pouvez vous me dire ce que vous voulez dire par "immutables" ? Qu'on ne
peut pas modifier ? C'est ce à quoi j'ai pensé au départ. Mais j'ai
l'impression que ce n'est pas ça car les variables de type entiers ou
float sont modifiables.


A mon tour:
Quand on fait "a=3" suivi de "a=4", on n'a pas changé le 3 en 4:
on a simplement déplacé la variable a pour qu'elle indique une nouvelle
valeur. La preuve:
a = 3
b = a
a = 4
print b
3 # qui n'a pas changé, merci.




Par contre, à partir de "a = [1,2,3]", si on fait "a[0] = 4",
l'affectation s'applique sur l'objet indiqué par a, et le modifie. La
preuve:
a = [1, 2, 3]
b = a
print b
[1, 2, 3]



a[0] = 4
print b
[4, 2, 3] # b change aussi!




C'est que a et b utilisent la même liste, et c'est elle que l'on a
modifié. Une liste est un objet mutable.
En python, les nombres (int, float, complex) et les chaines (str,
unicode) ne sont pas mutables: il n'y a aucune opération qui permet de
les modifier. C'est comme en Java, mais pas comme en C++.
La plupart des autres objets sont mutables; et je laisse à d'autres le
soin d'expliquer le cas du 'tuple'.


Une implémentation peut donc sans risque
utiliser un cache pour des instances de ces types, cf ma réponse à MC
pour ce qu'il en est avec CPython.


Je ne vais pas tarder à poster un fil sur ce genre de considérations,
mais je profite de cette remarque pour poser la question suivante :

id() donne un identifiant d'une variable. Un même id() pour deux
variable signifie que le contenu des variable est le même objet physique
dans la mémoire (si j'ai bien compris). Chez moi, id() ne donne pas une
adresse mémoire (il me semble en tout cas car j'ai un truc du genre
"3079857356L"). Existe-t-il une instruction pour avoir l'adresse mémoire
d'une variable ?


Une adresse mémoire c'est un nombre aussi... Ou bien préfères-tu
l'afficher en base 16?
print "0x%08x" % id(a)
0x00a659e0





--
Amaury



Avatar
bruno.desthuilliers
On 8 avr, 20:15, Francois wrote:

Il n'y a pas de "types simples" en Python - tout est objet. Par
contre, certains types - notamment les entier, les floats et les
chaines - sont immutables.


Pouvez vous me dire ce que vous voulez dire par "immutables" ? Qu'on ne
peut pas modifier ?


Oui.

C'est ce à quoi j'ai pensé au départ. Mais j'ai
l'impression que ce n'est pas ça car les variables de type entiers ou
float sont modifiables.


Ah bon ?-)

Non, les entiers, les floats et les chaines (et les tuples mais c'est
un poil plus complexe) sont immutables - l'état d'un de ces objets est
équivalent à sa valeur, et cet état n'est pas (je répète : n'est p as)
modifiable. On ne peux pas faire:

b = 'allo'
b[0] = 'z'

On peut toujours, bien sûr, rebinder un nom pointant vers un objet
immutable
vers un autre objet, mais ce n'est absolument pas la même chose.

Une implémentation peut donc sans risque
utiliser un cache pour des instances de ces types, cf ma réponse à M C
pour ce qu'il en est avec CPython.


Je ne vais pas tarder à poster un fil sur ce genre de considérations,
mais je profite de cette remarque pour poser la question suivante :

id() donne un identifiant d'une variable.


Non. id(obj) donne l'identifiant de obj ou de l'objet associé au nom
obj.

Un même id() pour deux
variable signifie que le contenu des variable


Les "variables" en Python ne sont pas des "conteneurs" (contrairement,
par exemple, au C). Ce sont des noms associés à des références sur d es
objets. Donc, un même id() pour deux noms signifie que les deux noms
référencent le même objet.

est le même objet


Oui.

physique
dans la mémoire (si j'ai bien compris).


Là c'est dépendant de l'implémentation. Mais concernant CPython, la
réponse est oui.

Chez moi, id() ne donne pas une
adresse mémoire (il me semble en tout cas car j'ai un truc du genre
"3079857356L"


C'est une adresse mémoire. En base 10.

). Existe-t-il une instruction pour avoir l'adresse mémoire
d'une variable ?


<mode="taquin">
Pour quelle définition de "variable" ? Le nom ? L'objet référencé pa r
le nom ? ou le couple formé par les deux ?-)
</mode>

Plus sérieusement: id() retourne l'identifiant unique d'un objet. En
CPython, cet identifiant est l'adresse mémoire, mais c'est un détail
d'implémentation.


Avatar
Francois
Merci beaucoup pour la réponse Amaury. Comme souvent, ça m'amène à
d'autres questions.

Pouvez vous me dire ce que vous voulez dire par "immutables" ? Qu'on
ne peut pas modifier ? C'est ce à quoi j'ai pensé au départ. Mais j'ai
l'impression que ce n'est pas ça car les variables de type entiers ou
float sont modifiables.


A mon tour:
Quand on fait "a=3" suivi de "a=4", on n'a pas changé le 3 en 4:
on a simplement déplacé la variable a pour qu'elle indique une nouvelle
valeur.


Tiens c'est marrent, moi j'aurais pas dit ça comme ça. J'aurais plutôt
dit «Avec a=4 (juste après a=3), la zone mémoire correspondant à la
variable a (qui contenait en binaire l'entier 3) est modifiée pour
contenir le code binaire de l'entier 4 ». Pour moi, il n'y aurait pas de
«déplacement» de variable comme tu le dis, mais réécriture de son
contenu, comme il me semble c'est le cas dans de nombreux langages (sans
aucune certitude). Mais j'ai l'impression que c'est toi qui as raison
voir à la fin du message pour un bout d'explication.

La preuve:
a = 3
b = a
a = 4
print b
3 # qui n'a pas changé, merci.





Ok, pour ce code. Mais, avec ma façon de voir (expliquée juste au
dessus), le résultat s'explique tout autant. Avec b=a, dans la zone
mémoire allouée à b, on écrit le contenu de la zone mémoire allouée à a.
Après, avec a=4, on modifie le contenu de a, mais celui de b, c'est
pourquoi b donne toujours 3. Ce que je veux dire, c'est que le code
ci-dessus n'est pas, pour moi, une preuve de ce que tu dis. Mais j'ai
peut-être rien compris (à ne pas exclure :-) ).


Par contre, à partir de "a = [1,2,3]", si on fait "a[0] = 4",
l'affectation s'applique sur l'objet indiqué par a, et le modifie. La
preuve:
a = [1, 2, 3]
b = a
print b
[1, 2, 3]



a[0] = 4
print b
[4, 2, 3] # b change aussi!




C'est que a et b utilisent la même liste, et c'est elle que l'on a
modifié. Une liste est un objet mutable.
En python, les nombres (int, float, complex) et les chaines (str,
unicode) ne sont pas mutables: il n'y a aucune opération qui permet de
les modifier. C'est comme en Java, mais pas comme en C++.
La plupart des autres objets sont mutables; et je laisse à d'autres le
soin d'expliquer le cas du 'tuple'.


Ok pour tout ça. Pas la peine de m'expliquer le 'tuple', j'ai cru
comprendre qu'il n'était pas "mutable". C'est étrange cette vision je
trouve. Une liste de int, sachant que les int ne sont pas mutables
(d'après ce que j'ai cru comprendre), est quant à elle "mutable" !
Autrement dit, une liste de trucs pas "mutables" peut donner un bidul
"mutable" ? Ça fait vraiment bizarre je trouve.


Je ne vais pas tarder à poster un fil sur ce genre de considérations,
mais je profite de cette remarque pour poser la question suivante :

id() donne un identifiant d'une variable. Un même id() pour deux
variable signifie que le contenu des variable est le même objet
physique dans la mémoire (si j'ai bien compris). Chez moi, id() ne
donne pas une adresse mémoire (il me semble en tout cas car j'ai un
truc du genre "3079857356L"). Existe-t-il une instruction pour avoir
l'adresse mémoire d'une variable ?


Une adresse mémoire c'est un nombre aussi... Ou bien préfères-tu
l'afficher en base 16?
print "0x%08x" % id(a)
0x00a659e0





Ok. Oui je préfère le voir en base 16. Ça me donne plus l'impression que
j'ai une adresse (qui est un nombre, on est bien d'accord). Donc, si je
comprends bien, id(a) c'est vraiment l'adresse mémoire du contenu de a.
C'est bien cela ?

Et là, il y a un truc qui m'étonne beaucoup. C'est ce code :

a = 4
print "L'adresse de a est : 0x%08x" %id(a)
# ce qui donne "L'adresse de a est : 0x0816df14"
a = 5
print "L'adresse de a est : 0x%08x" %id(a)
# ce qui donne "L'adresse de a est : 0x0816df08"

L'adresse de a aurait changer. La vache ! Ça me scie ! Comme tu disais,
la variable a aurait été «déplacée», Python modifie l'adresse de a pour
qu'elle «pointe» vers la valeur 5. Mais la valeur 4 n'a pas été effacée,
elle se ballade quelque part dans la mémoire. Ça me fait drôle qu'une
variable toute gentille comme a change d'adresse juste après a=5. C'est
possible d'avoir des explications ?

Merci encore pour tes explications. :-)



--
François




1 2