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

7 réponses

1 2
Avatar
Francois
On 8 avr, 20:15, Francois wrote:
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 pas)
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.


Donc quand je fais :

a = 4
a = 5

je n'ai pas modifié 4, j'ai "rebindé" (je ne connais pas ce terme) la
variable a. Je tente une définition de "rebinder" : faire pointer une
variable à un nouvel endroit de la mémoire (une contiendra 5 dans notre
exemple). C'est ça ?

Mais alors, la zone où il y a le code binaire de 4 (crée par a=4) ne
sera jamais réécrite un jour (puisque non modifiable) ?


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 des
objets. Donc, un même id() pour deux noms signifie que les deux noms
référencent le même objet.


Voilà. C'est pour ça que je suis perdu. Parce que c'est pas comme en C
(que je connais pas très bien d'ailleurs, mais qui est le seul langage
que je connaisse un peu pour l'instant). Au moins, je comprends pourquoi
je suis perdu.

En Python, si j'ai bien compris (n'hésitez pas à me rectifier) :

- Un nom de variable est stocké dans la mémoire. C'est une différence
avec le C, je crois bien. Ce nom est-il stocker dans la mémoire RAM ?

- Le nom d'une variable est associé à une référence sur un objet. Ok. Là
aussi, c'est pas comme le C où une variable est, si je ne dis pas de
bêtises, l'objet lui-même. Quand vous parlez de "nom de variable associé
à une référence", la variable est-elle en fait une sorte de pointeur
comme un pointeur en C ?

- Par contre, comme en C, l'objet à une adresse mémoire bien sûr, un
type (c'est la taille de la mémoire allouée et la manière dont le
contenu est interprété) et une valeur.


). 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é par
le nom ? ou le couple formé par les deux ?-)
</mode>


:-) Et oui. Mais c'est justement ce mode taquin qu'il me faudrait éclaircir.


--
François


Avatar
bruno.desthuilliers
On 8 avr, 22:36, Francois wrote:
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 nouve lle
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 ».


Ca, c'est du C.

Pour moi, il n'y aurait pas de
«déplacement» de variable comme tu le dis, mais réécriture de so n
contenu, comme il me semble c'est le cas dans de nombreux langages (sans
aucune certitude).


Dans tous les langages ayant des types 'immédiats' (ou 'primitifs').

Mais j'ai l'impression que c'est toi qui as raison


Je confirme tout de suite ton impression !-)

(snip - cf ma tentative d'explication dans un thread voisin initié par
l'OP)


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.



C'est surtout parce que tu a modifié la liste sur place, tu n'a pas
affecté une autre liste à a.

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".


Un tuple n'est pas mutable mais il peut contenir des objets mutables.

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.


Une liste est un objet qui contient d'autres objets. On peut y ajouter
des objets, en enlever, ou en remplacer certains par d'autres. Que les
objets en question soient mutables ou non est tout à fait orthogonal.

Je ne vais pas tarder à poster un fil sur ce genre de considération s,
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.


id(a) est l'identifiant de l'objet *référencé* (pas 'contenu') par a.
En CPython, cet identifiant est l'adresse mémoire.

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.


Ce n'est pas l'adresse de a (un nom n'a pas d'adresse), mais l'adresse
de l'objet référencé par a.

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.


Non. Python associe le nom 'a' à un autre objet (dont l'id est donc,
par définition, différent).

Mais la valeur 4


Laquelle ?-)

avant l'opération, 'a' référençait un objet de type int, pas une
'valeur' (au sens binaire). Après, 'a' référence un autre objet de
type int. Ce qu'est devenu le premier n'est pas de ton ressort, c'est
l'affaire de l'implémentation (ici, la machine virtuelle CPython).

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'es t
possible d'avoir des explications ?


Oui : 'a' n'a pas changé d'adresse, puisqu'un nom n'a pas d'adresse,
et que la notion d'adresse mémoire n'existe pas en Python (le langage
- of course, l'implémentation gère ces notions, mais comment elle le
gère n'est pas défini par le langage...).





Avatar
bruno.desthuilliers
On 8 avr, 22:59, Francois wrote:



On 8 avr, 20:15, Francois wrote:
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'e st pas)
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.


Donc quand je fais :

a = 4
a = 5

je n'ai pas modifié 4, j'ai "rebindé" (je ne connais pas ce terme)


très affreux anglicisme, mais je n'ai pas trouvé de traduction qui me
satisfasse pour le moment. Littéralement, 'to bind' = 'lier',
'binding' = 'liaison' et donc 'to rebind' = 'relier', mais 'relier' à
d'autres nuances en français.

La vérité est qu'en Python, il n'y a *pas* de variables. Du moins pas
au sens que tu connais. Il ya des 'bindings' (liaisons, ou encore
associations) entre des noms (qui ne sont rien d'autre que ça: des
noms) et des références (je préfère éviter de parler d'adresses
mémoires, qui relèvent de l'implémentation) sur des objets.
L'opérateur d'assignation ('=') associe un nom à une référence sur un
objet. Un même objet peut être référencé par plusieurs noms, un m ême
nom ne référence qu'un seul objet à un moment donné.

la
variable a. Je tente une définition de "rebinder" : faire pointer une
variable à un nouvel endroit de la mémoire


Je t'en propose une autre : réassocier le nom à une référence sur un
autre objet. Tant que tu t'accrochera à tes concepts venus du C, tu ne
pourra pas comprendre cet aspect de Python.

(une contiendra 5 dans notre
exemple). C'est ça ?

Mais alors, la zone où il y a le code binaire de 4 (crée par a=4) ne
sera jamais réécrite un jour (puisque non modifiable) ?


La zone où il y a l'objet de type int et de valeur 4 ne te concerne
pas, c'est l'affaire de la machine virtuelle. Dans le cas général,
chaque objet a un compteur de références, qui est incrémenté à cha que
fois qu'on associe un nom à cet objet et décrémenter chaque fois qu'on
supprime (par exemple par réassignation) une de ces associations.
Quand le compteur de références tombe à zéro, l'objet est
(généralement) libéré.

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


Voilà. C'est pour ça que je suis perdu. Parce que c'est pas comme en C


Non. Tu es perdu parce que tu essaie d'appliquer les concepts du C à
un modèle très différent. C'est un peu normal bien sûr, puisque les
mêmes termes sont employés, mais ne t'y trompe pas, les concepts ne
sont pas les mêmes.

(que je connais pas très bien d'ailleurs, mais qui est le seul langage
que je connaisse un peu pour l'instant). Au moins, je comprends pourquoi
je suis perdu.


C'est déjà un début !-)

En Python, si j'ai bien compris (n'hésitez pas à me rectifier) :

- Un nom de variable est stocké dans la mémoire.


Admettons...

C'est une différence
avec le C, je crois bien.


La norme C ne précise rien là-dessus.

Ce nom est-il stocker dans la mémoire RAM ?


C'est une affaire entre l'implémentation de Python que tu utilises et
l'OS hôte.

- Le nom d'une variable est associé à une référence sur un objet.
Une 'variable' est l'association d'un nom et d'une référence sur un

objet.

Ok. Là
aussi, c'est pas comme le C où une variable est, si je ne dis pas de
bêtises, l'objet lui-même.


Dans la plupart des implémentations connues de C, une 'variable' est
traduite par une adresse mémoire + une information de type, et la
valeur de la variable est le contenu de la mémoire à l'adresse en
question, après interprétation dépendante de l'information de type.

Quand vous parlez de "nom de variable associé
à une référence", la variable est-elle en fait une sorte de pointeur
comme un pointeur en C ?


cf ma réponse dans un thread voisin.

- Par contre, comme en C, l'objet à une adresse mémoire bien sûr, un
type (c'est la taille de la mémoire allouée et la manière dont le
contenu est interprété) et une valeur.


Non. L'objet est quelquepart (où, ce n'est pas ton affaire), et son
'type', c'est l'objet 'class' avec lequel il a été créé.

Après, bien sûr, *dans CPython* (qui comme son nom l'indique est écrit
en C), il existe des pointeurs sur des structures de données en
mémoire etc, mais rien de tout ça ne relève du langage Python *en tant
que langage* - il est important de distinguer un langage (une syntaxe
+ une grammaire) d'une implémentation de ce langage. A vrai dire, tout
ce que je dis ici et dans le thread voisin sur les variables en C est
dans l'absolu tout à fait incorrecte, car ça n'est pas défini par la
norme C - c'est juste comme ça que le C est ordinairement implémenté.



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

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.



Moi, j'avais trouvé ce texte :

dans (5.17 copy -- Shallow and deep copy operations) :
This module does not copy types like module, method, stack trace, stack
frame, file, socket, window, array, or any similar types. It does
``copy'' functions and classes (shallow and deeply), by returning the
original object unchanged; this is compatible with the way these are
treated by the pickle module. Changed in version 2.5: Added copying
functions.

La dernière phrase m'avait incité à penser que le copie de fonctions
était possible en Python 2.5...

C'est assez curieux, cette différence de documentation...

Néanmoins, il est précisé que, pour les fonctions, c'est l'original qui
est retourné. Mais, je trouve ambigü que le paragraphe soit suffixé avec
"Changed in version 2.5... "






Pour les decorators. Teste ces deux morceaux de code :


def deco(f):
print f.__name__
return f

@deco
def func(): pass

print "AAA"
func()
print "BBB"
func()
print "CCC"
func()
print "DDD"




def deco(f):
print f.__name__
return f

def func(): pass

print "AAA"
deco(func)()
print "BBB"
deco(func)()
print "CCC"
deco(func)()
print "DDD"




Si j'ai bien compris ton propos, ils devraient donner le même résultat.
Or, ce n'est pas le cas.



@+
--
Michel Claveau

Avatar
bruno.desthuilliers
On 9 avr, 00:10, "Méta-MCI (MVP)"
wrote:
Bonsoir !

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.


Moi, j'avais trouvé ce texte :

dans (5.17 copy -- Shallow and deep copy operations) :
This module does not copy types like module, method, stack trace, stack
frame, file, socket, window, array, or any similar types. It does
``copy'' functions and classes (shallow and deeply), by returning the
original object unchanged; this is compatible with the way these are
treated by the pickle module. Changed in version 2.5: Added copying
functions.

La dernière phrase m'avait incité à penser que le copie de fonctions
était possible en Python 2.5...

C'est assez curieux, cette différence de documentation...


Je viens de vérifier, je confirme t'avoir cité la doc de la 2.5.1.
Mais c'est celle fournie par help(copy), donc elle n'a peut-être pas
été mise à jour.

Néanmoins, il est précisé que, pour les fonctions, c'est l'original qui
est retourné. Mais, je trouve ambigü que le paragraphe soit suffixé avec
"Changed in version 2.5... "


Avec python2.4, j'ai une erreur en essyant de passer une fonction à
copy.[deep]copy().

Pour les decorators. Teste ces deux morceaux de code :

def deco(f):
print f.__name__
return f


défini une fonction 'deco' qui affiche sur la sortie standard la
valeur de l'attribut __name__ de son argument, puis retourne son
argument inchangé.

Pas un décorateur très utile, accessoirement, mais bon...

@deco
def func(): pass


Lors de l'évaluation du script/module, appelle la fonction deco avec
func comme arguement - ce qui entraine l'affichage de 'func' sur la
sortie standard -, et assigne le résultat de cet appel - c'est à dire,
vu l'implémentation de deco(), la fonction func originale,
parfaitement inchangée - au nom global 'func'.

print "AAA"
func()


Appelle la fonction func(), qui retourne None, donc rien ne s'affiche.
Normal...

(snip)

def deco(f):
print f.__name__
return f


pareil

def func(): pass


pareil
print "AAA"
deco(func)()


Appelle deco avec func comme argument - ce qui entraine l'affichage du
func.__name__ sur la sortie standard - puis appelle la valeur
retournée par deco - c'est à dire la fonction func() - ce qui n'a
aucun effet sensible (à part sur la consommation CPU) puisque func()
ne fait rien.

(et on recommence....)


Si j'ai bien compris ton propos, ils devraient donner le même résultat .


Je ne vois pas comment ces deux snippets pourraient produire le même
résultat. Dans le premier cas, tu appelle la fonction deco une seule
fois, puis tu alterne des affichages sur la sortie standard avec des
appels à la fonction func, dans le second tu alternes des affichages
sur la sortie standard avec des appels à la fonction deco avec func
comme argument chainés à des appels à la fonction func elle-même.

Ca ne change rien au fait que

@deco
def func(): pass

et

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

soient *strictement* équivalents.

Accessoirement, avant l'introduction de la syntaxe @decorator dans
Python 2.4, pour les classmethods et staticmethods, on employait la
seconde syntaxe.

Or, ce n'est pas le cas.


Oui, et c'est tout à fait normal. Ta fonction deco() retournant son
argument inchangé, tu t'attendais à quoi ?

Si tu veux afficher le nom de la fonction décorée à chaque appel *de
la fonction décorée* (et non à chaque appel du décorateur), il faut
utiliser une fermeture (ce qui est généralement le cas avec les
décorateurs BTW), ie:

def deco(func):
def decorated(*args, **kw):
print func.__name__
return func(*args, **kw)
return decorated

En tout état de cause, ça n'a aucun rapport avec le compilo... Et ça
ne nous dit pas non plus pourquoi tu veux cloner une fonction !-)


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

Et ça ne nous dit pas non plus pourquoi tu veux cloner une fonction


Deux explications :

1) Je trouve que les fonctions, c'est (aussi) un moyen (un objet) très
pratique de créer des singletons. Je les utilise donc (aussi) pour ça.
Mais, dans quelques cas, ça me serait pratique d'avoir plusieurs
instances de ces objets (ça revient à faire du prototypage).

Voilà pour l'explication sérieuse (?).

2) j'ai la fonction suivante :
def Dolly( cri='Bêêê bêêêh !')
parler( cri)
Or, j'ai entendu dire que les écossais avaient réussi à cloner Dolly.
Pourquoi pas moi ?


@-salutations
--
Michel Claveau

Avatar
Bruno Desthuilliers
Salut !

Et ça ne nous dit pas non plus pourquoi tu veux cloner une fonction


Deux explications :

1) Je trouve que les fonctions, c'est (aussi) un moyen (un objet) très
pratique de créer des singletons.


Admettons...

Je les utilise donc (aussi) pour ça.
Mais, dans quelques cas, ça me serait pratique d'avoir plusieurs
instances de ces objets (ça revient à faire du prototypage).


Bin la solution simple reste de définir une classe (éventuellement
appelable).

Après, y a probablement des solutions tordues, incompréhensibles,
impossible à maintenir et digne de rub goldberg (je peux en imaginer au
moins deux, toutes les deux également digne d'une entrée dans le
DailyWTF), mais à part en tant qu'exercice de style, je ne vois vraiment
pas l'intérêt de se compliquer la vie à faire compliqué quand il est si
simple de faire simple.

Puis-je très respectueusement te suggérer de lancer ton shell Python,
d'exécuter l'instruction 'import this', et de relire attentivement ?
(nb: si tu es toujours aussi faché avec l'english, tu trouvera dans les
archives de ce groupe un lien vers une trad française).

Voilà pour l'explication sérieuse (?).


Le point d'interrogation est effectivement de rigueur...

2) j'ai la fonction suivante :
def Dolly( cri='Bêêê bêêêh !')
parler( cri)
Or, j'ai entendu dire que les écossais avaient réussi à cloner Dolly.


Là, faut utiliser un algorithme génétique...

Pourquoi pas moi ?


Parce que là où ya du gène, ya pas de plaisir ?

ok --->[]


1 2