LangageComposition

[wiki: Les objets composés] [#license ©]

PageOutline

Généralités

Un objet peut contenir des objets, qui peuvent contenir des objets, qui… On peut descendre dans les niveaux successifs en enchainant les . .

#!div style="width:600px"


{{{
#!python
class Vecteur: pass
class Impulsion : pass

imp = Impulsion()
imp.vitesse = Vecteur()
imp.vitesse.x = 1.
imp.vitesse.y = 0.
imp.vitesse.z = 0.
imp.masse = 10.

print(imp.vitesse.x,imp.vitesse.y,imp.vitesse.z,imp.masse)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
1.0 0.0 0.0 10.0
</pre>

}}}

(Non)Chainage des constructeurs

Dans un constructeur, il n’y a pas d’appel automatique aux constructeurs des sous-parties de l’objet courant, comme cela existe dans d’autres langages. Un objet n’a pas d’obligation de contenir des données, et donc tout ce qui est ajouté doit l’être explicitement, y compris dans les constructeurs.

#!div style="width:600px"


{{{
#!python
class Vecteur:
def init(self):
self.x = 0
self.y = 0
self.z = 0
def str(self):
return "{:d}|{:d}|{:d}".format( self.x, self.y, self.z )

class Impulsion:
def init(self):
self.vitesse = Vecteur()
self.masse = 0
def str(self):
return "{:s}|{:d}".format( self.vitesse.str(), self.masse )

i1 = Impulsion()
print(i1)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
0|0|0|0
</pre>

}}}

Du point de vue de l’affectation, de la copie, de la comparaison, et de beaucoup d’autres opérations, un objet se comporte essentiellement comme un dictionnaire contenant des attributs.

Affectation et copie

Lorsqu’on affecte un objet à une variable, il n’y a pas de copie. Comme d’habitude, cela génère une nouvelle variable qui référence le même objet original. Dans l’exemple ci-dessous, on voit que si je modifie un attribut de i2, i1 est également modifié.

#!div style="width:600px"


{{{
#!python
i2 = i1
i2.masse = 1
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|0|0|1), (0|0|0|1)
</pre>

}}}

Pour dupliquer un objet, on peut avoir recours au module copy, et à sa fonction copy(). Ainsi, si je modifie maintenant un attribut de i2, l’attribut correspondant de i1 est inchangé.

#!div style="width:600px"


{{{
#!python
import copy
i2 = copy.copy(i1)
i2.masse = 2
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|0|0|2), (0|0|0|1)
</pre>

}}}

Mais il s’agit d’une copie de surface. L’équivalent d’une affectation des attributs un à un :

#!div style="width:600px"


{{{
#!python
i2 = Impulsion()
i2.vitesse = i1.vitesse # i2 = copy.copy(i1)
i2.masse = i1.masse # i2 = copy.copy(i1)
i2.masse = 2
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|0|0|2), (0|0|0|1)
</pre>

}}}

Comme au début, pour i2 = i1, l’instruction i2.vitesse = i1.vitesse ne duplique pas l’instance de Vecteur, mais fait en sorte que i2.vitesse et i1.vitesse désigne la même instance. Ainsi, si on change un attribut de i2.vitesse, on agit également sur i1.vitesse :

#!div style="width:600px"


{{{
#!python
i2.vitesse.z = 2
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|0|2|2), (0|0|2|1)
</pre>

}}}

Si on souhaite dupliquer intégralement un objet et tous ses attributs à tous les niveaux, on préfèrera la fonction deepcopy(). Dans l’exemple suivant, on modifie un sous-sous-attribut de i2, sans affecter i1.

#!div style="width:600px"


{{{
#!python
i2 = copy.deepcopy(i1)
i2.vitesse.y = 3
print("({}), ({})".format(i2,i1))


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(0|3|2|1), (0|0|2|1)
</pre>

}}}

Egalité et identité de deux objets

Malheureusement, l’opérateur de comparaison == ne va pas spontanément vérifier récursivement l’égalité de tous les données membres de l’objet. A la place, il se contente de vérifier que les deux variables désignent le même objet, à la façon d’un is :

#!div style="width:600px"


{{{
#!python
i1 = Impulsion(1,2,3,4)

import copy
i2 = copy.deepcopy(i1)

print(i1 is i2, i1==i2, i1!=i2)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
False, False, True
</pre>

}}}

Pour obtenir le comportement « logique », il faut par exemple redéfinir les opérateurs eq et ne à tous les niveaux imbriqués :

#!div style="width:600px"


{{{
#!python
class Vecteur:
.....
def eq(self,other):
return ( ( self.x==other.x )
and ( self.y==other.y )
and ( self.z==other.z ) )

class Impulsion:
.....
def eq(self,other):
return ( ( self.vitesse==other.vitesse )
and ( self.masse==other.masse ) )

i1 = Impulsion(1,2,3,4)

import copy
i2 = copy.deepcopy(i1)

print(i1 is i2, i1==i2, i1!=i2)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
False, True, True
</pre>
#!python
class Vecteur:
    .....
    def ne(self,other): return not(self==other)
        
class Impulsion:
    .....
    def ne(self,other): return not(self==other)


i1 = Impulsion(1,2,3,4)

import copy
i2 = copy.deepcopy(i1)

print(i1 is i2, i1==i2, i1!=i2)


#!html
<pre  style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
False, True, False
</pre>

}}}

Travaux Pratiques

1. Dotez la classe Expression d’un opérateur + de concaténation.
1. Dotez la classe Expression d’une méthode de comparaison ==.
1. Ajouter le code suivant en fin de analyseur.py afin de vérifier les opérations ci-dessus :

#!python
    if name == "main":
        expr1 = Expression("+1+2")
        expr2 = Expression("+3+4")
        expr3 = expr1+expr2
        expr4 = Expression("+1+2+3+4")
        print(expr3)
        print(expr4)
        if (expr3==expr4): print("Concatenation et comparaison OK")
        else: print("PROBLEME de concatenation et comparaison")

© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]


Attachments