OVH Cloud OVH Cloud

Heritage de methode

31 réponses
Avatar
Francois
Bonjour,

J'ai une classe 'Vecteur' qui m'intéresse et à laquelle je souhaite
rajouter quelques méthodes.
Comme cette classe est 'figée' et que je ne peux pas y toucher, j'ai
pensé créer une classe 'MonVecteur' qui dérive de cette classe et à
laquelle je rajoute mes propres méthodes.

En particulier je souhaiterais néanmoins que 'MonVecteur' dispose des
mêmes méthodes que 'Vecteur', pour les additions, les
multiplications, les normalisations, etc...
Le problème est que ces méthodes _créent_ un objet de classe 'Vecteur'
et non pas 'MonVecteur' lorsque j'essaye par exemple d'additioner deux
objets de classe 'MonVecteur'.

J'espère avoir été clair, donc si quelqu'un avait une idée pour
résoudre mon problème...

Merci d'avance,

10 réponses

1 2 3 4
Avatar
Christophe Cavalaria
Francois wrote:

On Sat, 26 Nov 2005 10:51:27 +0100, Christophe Cavalaria

En gros, tu suggères de systématiser la délégation. C'est ça ?
Mais, par rapport à l'héritage, il y aura une différence, si la
classe Vecteur évolue (si une nouvelle version contient une
nouvelle fonction, par exemple).



Bien sur, car quand la classe vector evolue, tu dois surveiller toute
nouvelle méthode et chercher celles qui renvoient un nouveau objet
Vector pour les remplacer par des versions qui renvoient un objet
MonVector de tt façon.

Une autre possibilité aurait été si la classe Vector fesait
self.__class__() pour construire ses nouvelles valeurs mais je doute
que ce soit le cas. Dans ce dernier cas, il est envisageable
d'hériter.

De toute façon, plus on n'y pense et plus on voit que les classes de
type vector/string/num etc... ne sont pas de bons candidats pour
faire de l'heritage. Trop de problèmes d'impedence entre l'ancien
type et le nouveau type.


Si la classe Vecteur faisait un self.__class__() avant toute création
de nouvelles valeurs, cela résoudrait le problème c'est ça ?
Est-ce que cela vaut-il l'effort d'inclure cela dans tous ce que je
développe ? Quels sont les inconvénients ?

J'aime assez bien le principe...

Merci !


Le self.__class__() est juste une idée que je viens d'avoir sur le coup. Je
ne peux décemment pas conseiller de l'utiliser avant d'avoir bien eu le
temps de reflechir aux conséquences de ce pattern :)

Pour ton problème de Vecteur que tu veux faire évoluer, on peut résumer la
difficulté de cette façon : la classe Vecteur à une sémantique de valeur.

Comment détecter une classe avec sémantique de valeur ? C'est facile, il y a
quelques points qui ne trompent pas :
- beaucoup de fonctions membres qui retournent de nouvelles instances de la
classe
- la classe est immutable ( comme les tuples et les string en Python ) ou
elle pourrait l'être très facilement sans consequence majeure
- il y a beaucoup de fonctions externes qui renvoient une nouvelle instance
de la classe en résultat
- l'instance n'est rien, seul l'état compte

Les points 1 et 3 posent de gros problèmes pour faire de l'heritage. Mais si
la classe est immutable, alors seul le point 1 peut se contourner. Il reste
alors le point 3.

A noter qu'en Python, il y a un cas concret du gène que pose l'usage de
l'héritage d'une classe à sémantique de valeur : la cohabitation entre str
et unicode.



Avatar
bruno at modulix
Do Re Mi chel La Si Do wrote:
Bonjour !

En gros, tu suggères de systématiser la délégation. C'est ça ?
Mais, par rapport à l'héritage, il y aura une différence, si la classe
Vecteur évolue (si une nouvelle version contient une nouvelle fonction, par
exemple).


Où est le problème ? Les mécanismes de délégation générique de Python
(__getattr__/__settattr__ etc) sont assez puissants pour gérer ça de
façon transparente.



@-salutations

Michel Claveau






--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"

Avatar
Christophe
Do Re Mi chel La Si Do wrote:

Bonjour !

En gros, tu suggères de systématiser la délégation. C'est ça ?
Mais, par rapport à l'héritage, il y aura une différence, si la classe
Vecteur évolue (si une nouvelle version contient une nouvelle fonction, par
exemple).



Où est le problème ? Les mécanismes de délégation générique de Python
(__getattr__/__settattr__ etc) sont assez puissants pour gérer ça de
façon transparente.


Comme je l'ai déjà dis, un des problèmes de ce genre qui necessitent
d'encapsuler à la main toutes les fonctions de Vecteur c'est qu'il y en
a beaucoup qui créent un nouveau vecteur en retour. Il ne suffit pas de
passer directement le signal à la classe Vecteur et il faut aussi
interpreter son résultat.


Avatar
bruno at modulix
Christophe wrote:

Do Re Mi chel La Si Do wrote:

Bonjour !

En gros, tu suggères de systématiser la délégation. C'est ça ?
Mais, par rapport à l'héritage, il y aura une différence, si la
classe Vecteur évolue (si une nouvelle version contient une nouvelle
fonction, par exemple).



Où est le problème ? Les mécanismes de délégation générique de Python
(__getattr__/__settattr__ etc) sont assez puissants pour gérer ça de
façon transparente.


Comme je l'ai déjà dis, un des problèmes de ce genre qui necessitent
d'encapsuler à la main toutes les fonctions de Vecteur


Pas en Python.

c'est qu'il y en
a beaucoup qui créent un nouveau vecteur en retour. Il ne suffit pas de
passer directement le signal à la classe Vecteur et il faut aussi
interpreter son résultat.


Pas un problème. Tu veux la solution toute faite ou tu préfères chercher
par toi-même ?-)

Allez, je te donne une ou deux pistes:
- une méthode de Vector est à l'origine une fonction, elle-même instance
de la classe function et attribut de la classe Vector (laquelle classe
est elle-même un objet...)
- getattr() marche avec tous les types d'attributs
- n'importe quel objet Python peut-être utilisé comme fonction, à
condition d'imlémenter __call__()
- callable(obj) permet de savoir si un objet implémente __call__

Un bémol: si la classe Vector est codée en C et ne respecte pas
pleinement le fonctionnement des classes "newstyle", je ne garantis rien.


--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"



Avatar
Christophe
Christophe wrote:



Do Re Mi chel La Si Do wrote:


Bonjour !

En gros, tu suggères de systématiser la délégation. C'est ça ?
Mais, par rapport à l'héritage, il y aura une différence, si la
classe Vecteur évolue (si une nouvelle version contient une nouvelle
fonction, par exemple).



Où est le problème ? Les mécanismes de délégation générique de Python
(__getattr__/__settattr__ etc) sont assez puissants pour gérer ça de
façon transparente.



Comme je l'ai déjà dis, un des problèmes de ce genre qui necessitent
d'encapsuler à la main toutes les fonctions de Vecteur



Pas en Python.


c'est qu'il y en
a beaucoup qui créent un nouveau vecteur en retour. Il ne suffit pas de
passer directement le signal à la classe Vecteur et il faut aussi
interpreter son résultat.



Pas un problème. Tu veux la solution toute faite ou tu préfères chercher
par toi-même ?-)

Allez, je te donne une ou deux pistes:
- une méthode de Vector est à l'origine une fonction, elle-même instance
de la classe function et attribut de la classe Vector (laquelle classe
est elle-même un objet...)
- getattr() marche avec tous les types d'attributs
- n'importe quel objet Python peut-être utilisé comme fonction, à
condition d'imlémenter __call__()
- callable(obj) permet de savoir si un objet implémente __call__

Un bémol: si la classe Vector est codée en C et ne respecte pas
pleinement le fonctionnement des classes "newstyle", je ne garantis rien.


Tu n'as pas bien compris ce que je voulais dire. Voici un example de
classe Vecteur :

class Vecteur:
def __init__(self, x, y, z):
self.x, self.y, self.z = x,y,z
def scale(self, scale):
return Vecteur(self.x*scale, self.y*scale, self.z*scale)

class MonVecteur(Vecteur):
def affiche_valeur(self):
print "MonVecteur : ", self.x, self.y, self.z

a = MonVecteur(1,2,3)
b = a.scale(10)
b.affiche_valeur() # Erreur : pas un MonVecteur




Avatar
Do Re Mi chel La Si Do
Salut !

Et avec ça :
def scale(self, scale):
return self
Avatar
Do Re Mi chel La Si Do
Pardon, j'ai lu trop vite !
Avatar
bruno at modulix
Christophe wrote:
(snip)
Un bémol: si la classe Vector est codée en C et ne respecte pas
pleinement le fonctionnement des classes "newstyle", je ne garantis rien.



Tu n'as pas bien compris ce que je voulais dire. Voici un example de
classe Vecteur :

class Vecteur:
def __init__(self, x, y, z):
self.x, self.y, self.z = x,y,z
def scale(self, scale):
return Vecteur(self.x*scale, self.y*scale, self.z*scale)


BTW, j'en profite pour recommander la BonnePratique(tm) suivante : ne
pas coder le nom de la classe en dur dans le code. Christophe n'aurait
pas ce problème si scale() avait été implémenté ainsi:

def scale(self, scale):
return self.__class__(self.x*scale, self.y*scale, self.z*scale)


class MonVecteur(Vecteur):
def affiche_valeur(self):
print "MonVecteur : ", self.x, self.y, self.z

a = MonVecteur(1,2,3)
b = a.scale(10)
b.affiche_valeur() # Erreur : pas un MonVecteur


Christophe, j'ai *parfaitement* compris ce que tu voulais dire !-)

Et je maintiens que c'est facile à résoudre d'une façon générique (ie:
sans devoir tout recoder à la main) en utilisant la délégation et
__getattr__. (NB : au bémol ci-dessus près)


--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"


Avatar
Christophe
Christophe wrote:
(snip)

Un bémol: si la classe Vector est codée en C et ne respecte pas
pleinement le fonctionnement des classes "newstyle", je ne garantis rien.



Tu n'as pas bien compris ce que je voulais dire. Voici un example de
classe Vecteur :

class Vecteur:
def __init__(self, x, y, z):
self.x, self.y, self.z = x,y,z
def scale(self, scale):
return Vecteur(self.x*scale, self.y*scale, self.z*scale)



BTW, j'en profite pour recommander la BonnePratique(tm) suivante : ne
pas coder le nom de la classe en dur dans le code. Christophe n'aurait
pas ce problème si scale() avait été implémenté ainsi:

def scale(self, scale):
return self.__class__(self.x*scale, self.y*scale, self.z*scale)


Facile à dire.

class MonVecteur(Vecteur):
def affiche_valeur(self):
print "MonVecteur : ", self.x, self.y, self.z

a = MonVecteur(1,2,3)
b = a.scale(10)
b.affiche_valeur() # Erreur : pas un MonVecteur



Christophe, j'ai *parfaitement* compris ce que tu voulais dire !-)

Et je maintiens que c'est facile à résoudre d'une façon générique (ie:
sans devoir tout recoder à la main) en utilisant la délégation et
__getattr__. (NB : au bémol ci-dessus près)


Dans le cas ou Vecteur est déjà écrite de façon incorrecte, je suis
curieux de voir comment tu t'en sors ;)



Avatar
bruno at modulix
Christophe wrote:

Christophe wrote:
(snip)

Un bémol: si la classe Vector est codée en C et ne respecte pas
pleinement le fonctionnement des classes "newstyle", je ne garantis
rien.




Tu n'as pas bien compris ce que je voulais dire. Voici un example de
classe Vecteur :

class Vecteur:
def __init__(self, x, y, z):
self.x, self.y, self.z = x,y,z
def scale(self, scale):
return Vecteur(self.x*scale, self.y*scale, self.z*scale)




BTW, j'en profite pour recommander la BonnePratique(tm) suivante : ne
pas coder le nom de la classe en dur dans le code. Christophe n'aurait
pas ce problème si scale() avait été implémenté ainsi:

def scale(self, scale):
return self.__class__(self.x*scale, self.y*scale, self.z*scale)



Facile à dire.

class MonVecteur(Vecteur):
def affiche_valeur(self):
print "MonVecteur : ", self.x, self.y, self.z

a = MonVecteur(1,2,3)
b = a.scale(10)
b.affiche_valeur() # Erreur : pas un MonVecteur




Christophe, j'ai *parfaitement* compris ce que tu voulais dire !-)

Et je maintiens que c'est facile à résoudre d'une façon générique (ie:
sans devoir tout recoder à la main) en utilisant la délégation et
__getattr__. (NB : au bémol ci-dessus près)



Dans le cas ou Vecteur est déjà écrite de façon incorrecte, je suis
curieux de voir comment tu t'en sors ;)


class Vector:
def __init__(self, x, y, z):
self.x, self.y, self.z = x,y,z

def scale(self, scale):
return Vector(self.x*scale, self.y*scale, self.z*scale)


class MyVector(object):
def __init__(self, x, y, z):
self._vector = Vector(x, y, z)

def _verifyVector(self, obj):
if callable(obj):
return self._methodWrapper(obj)
elif isinstance(obj, Vector):
return self.__class__(obj.x, obj.y, obj.z)
else:
return obj

def _methodWrapper(self, fun):
def _wrapped(*args, **kwargs):
res = fun(*args, **kwargs)
return self._verifyVector(res)
return _wrapped

def __getattr__(self, name):
try:
attr = getattr(self._vector, name)
except AttributeError:
raise AttributeError("object %s has no attribute %s"
% (self, name))
return self._verifyVector(attr)

if __name__ == '__main__':
v1 = MyVector(1, 2, 3)
v2 = v1.scale(42)
assert isinstance(v2, MyVector)



Ca te va ?-)

--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"




1 2 3 4