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

Jouter des méthodes à une classe d'un module (perso)

13 réponses
Avatar
moky
Bonjour à toutes et à tous


J'ai un certain nombre de scripts qui me servent à manipuler des fichiers.

J'ai donc créé un module qui définit les classes le plus utiles. Mais
pour certaines applications, j'ai besoin de leur ajouter des méthodes en
plus.
Je peux faire hériter (voir l'exemple plus bas), mais je me heurte à un
problème : certaines fonctions et méthodes de mon module retournent des
instances des classes de mon module. Et pour elles, je ne peux pas
utiliser les méthodes supplémentaires.


Mettons que ClasseFoo soit une classe dans le module ModuleFoo, et que
la fonction FonctionFoo retourne une instance de ClasseFoo.

Dans mon programme principal je voudrais être capable de
1. Ajouter une méthode MethodePlus à la classe ClasseFoo (héritage, ok)
2. Utiliser la méthode MethodePlus sur les objets créés par FonctionFoo.
(pas ok parce que FonctionFoo retourne un objet qui ne profite pas de
l'héritage)




Par exemple, la classe Repertoire dans mon module perso manip


-------------- Extrait de manip.py ----------------

class Repertoire (object):
def __init__ (self,nom):
self.chemin = nom
self.existe = os.path.isdir(self.chemin)

-------------- fin de l'extrait ----------------

Maintenant, dans certains scripts utilisant cette classe, j'ai besoin
d'une ou deux méthodes très spécifiques supplémentaires.

Mettons que dans mon programme principal j'aie besoin d'une classe qui
contient exactement les méthodes de Repertoire, mais avec une méthode
"essai" en plus. Je fais ceci :

# manip est le nom du module
class Repertoire(manip.Repertoire):
def __init__(self,nom):
manip.Repertoire.__init__(self,nom)
def essai(self):
return "ici "+self.chemin


Faire
x = Repertoire("blable")
print x.code()
produit effectivement la sortie
ici blabla


Jusque là, ça va.
Maintenant le problème est que mon module a une fonction qui retourne un
objet de la classe Repertoire, par exemple


----------- Encore dans le module ---------
def Home(utilisateur):
return Repertoire("/home/"+utilisateur)
----------- Fin de l'extrait ---------


Dans mon programme principal, maintenant je fais

x = Home("moi") # tout se passe bien
print x.chemin # L'héritage fait correctement afficher /home/moi
print x.code

La dernière ligne ne fonctionne plus parce que x est une instance de la
classe Repertoire du module, et non de mon programme principal.



Comment faire pour ajouter des méthodes ? Repenser mon module/programme
autrement ?
Je n'ai pas très envie de mettre toutes les méthodes dans le module,
parce qu'alors, pour peu que je sois en train de travailler sur une
méthode qui plante pour un script, plus aucun des scipts utilisant le
module ne fonctionne.

Merci pour vos lumières
Bonne aprème
Laurent

10 réponses

1 2
Avatar
moky
Je me réponds à moi-même...
RTFM et lis les archives du groupe, putain de faignant !!
La réponse est ici toute cuite dans le fil "Heritage de methode" :
http://groups.google.fr/group/fr.comp.lang.python/browse_thread/thread/6e67278505358b4a/f44e2c65131a2a76?hl=fr&lnk=gst&q=ajouter+des+méthodes#f44e2c65131a2a76
Avatar
OdarR
On 24 mar, 14:38, moky wrote:
Je me réponds à moi-même...
RTFM et lis les archives du groupe, putain de faignant !!
La réponse est ici toute cuite dans le fil "Heritage de methode"  :ht tp://groups.google.fr/group/fr.comp.lang.python/browse_thread/threa...thode s#f44e2c65131a2a76



fort hein :-)
tu peux aussi passer une function (def ...) en paramètre dans ton
constructeur, éventuellement...

les functions se trimballent comme des objets :-)

Olivier
Avatar
moky
OdarR a écrit :
On 24 mar, 14:38, moky wrote:
Je me réponds à moi-même...
RTFM et lis les archives du groupe, putain de faignant !!
La réponse est ici toute cuite dans le fil "Heritage de methode" :http://groups.google.fr/group/fr.comp.lang.python/browse_thread/threa...thodes#f44e2c65131a2a76



fort hein :-)



oui :-) :-) :-)
Je suis *vraiment* impressionné !

tu peux aussi passer une function (def ...) en paramètre dans ton
constructeur, éventuellement...




Je vois moins comment cela s'adapte à mon problème : je voudrais que
*toutes* les instances de ma classe dans mon programme principal soient
avec les méthodes supplémentaires.
À moins de rajouter des choses directement dans manip.Repertoire.__init__

Laurent
Avatar
Bruno Desthuilliers
OdarR a écrit :
(snip)
tu peux aussi passer une function (def ...) en paramètre dans ton
constructeur, éventuellement...

les functions se trimballent comme des objets :-)



Ce qui est normal, vu que ce *sont* des objets.
Avatar
Bruno Desthuilliers
moky a écrit :
OdarR a écrit :
On 24 mar, 14:38, moky wrote:
Je me réponds à moi-même...
RTFM et lis les archives du groupe, putain de faignant !!
La réponse est ici toute cuite dans le fil "Heritage de methode"
:http://groups.google.fr/group/fr.comp.lang.python/browse_thread/threa...thodes#f44e2c65131a2a76




fort hein :-)



oui :-) :-) :-)
Je suis *vraiment* impressionné !

tu peux aussi passer une function (def ...) en paramètre dans ton
constructeur, éventuellement...




Je vois moins comment cela s'adapte à mon problème : je voudrais que
*toutes* les instances de ma classe dans mon programme principal soient
avec les méthodes supplémentaires.
À moins de rajouter des choses directement dans manip.Repertoire.__init__



Attention, les fonctions ne deviennent des méthodes que si elles sont
résolues comme attributs de *classe*. Une fonction résolue comme
attribut d'instance reste une fonction.
Avatar
Bruno Desthuilliers
moky a écrit :
Bonjour à toutes et à tous


J'ai un certain nombre de scripts qui me servent à manipuler des fichiers.

J'ai donc créé un module qui définit les classes le plus utiles. Mais
pour certaines applications, j'ai besoin de leur ajouter des méthodes en
plus.
Je peux faire hériter (voir l'exemple plus bas), mais je me heurte à un
problème : certaines fonctions et méthodes de mon module retournent des
instances des classes de mon module. Et pour elles, je ne peux pas
utiliser les méthodes supplémentaires.



Si tu a la main sur le module en question, tu peux éventuellement rendre
la (les) classe(s) à instancier paramétrable:


Mettons que ClasseFoo soit une classe dans le module ModuleFoo, et que
la fonction FonctionFoo retourne une instance de ClasseFoo.


>
Dans mon programme principal je voudrais être capable de
1. Ajouter une méthode MethodePlus à la classe ClasseFoo (héritage, ok)
2. Utiliser la méthode MethodePlus sur les objets créés par FonctionFoo.
(pas ok parce que FonctionFoo retourne un objet qui ne profite pas de
l'héritage)




La solution simple: passer la classe à instancier en paramètre à
fonction_foo:

def fonction_foo(arg1, argn, class_=ClasseFoo):
# code ici
return class_(...)


Si ça ne suffit pas, il y a d'autres solutions plus complexes
(wrapper+délégation dans le code client, pattern Factory avec
paramétrage par le code client - et au pire un monkeypatch) - à voir en
fonction du cas d'utilisation réel.
Avatar
moky
Bruno Desthuilliers a écrit :
moky a écrit :
Bonjour à toutes et à tous


J'ai un certain nombre de scripts qui me servent à manipuler des
fichiers.

J'ai donc créé un module qui définit les classes le plus utiles. Mais
pour certaines applications, j'ai besoin de leur ajouter des méthodes
en plus.
Je peux faire hériter (voir l'exemple plus bas), mais je me heurte à
un problème : certaines fonctions et méthodes de mon module retournent
des instances des classes de mon module. Et pour elles, je ne peux pas
utiliser les méthodes supplémentaires.



Si tu a la main sur le module en question, tu peux éventuellement rendre
la (les) classe(s) à instancier paramétrable:


Mettons que ClasseFoo soit une classe dans le module ModuleFoo, et que
la fonction FonctionFoo retourne une instance de ClasseFoo.


>
Dans mon programme principal je voudrais être capable de
1. Ajouter une méthode MethodePlus à la classe ClasseFoo (héritage, ok)
2. Utiliser la méthode MethodePlus sur les objets créés par
FonctionFoo. (pas ok parce que FonctionFoo retourne un objet qui ne
profite pas de l'héritage)




La solution simple: passer la classe à instancier en paramètre à
fonction_foo:

def fonction_foo(arg1, argn, class_=ClasseFoo):
# code ici
return class_(...)



J'ai résolu mon problème en ajoutant des méthodes dans le programme
principal.

J'ai par exemple, dans manip.py, la classe Fichier, à qui je veux
ajouter la méthode "code" pour un certain script.
Je mets ceci dans ledit script :


def codeFichier(self):
print "mon nom est : "+self.chemin
manip.Fichier.code = codeFichier

La méthode "chemin" est définie dans la classe manip.Fichier (de base).

Avec ça, ça fonctionne très bien.
Dois-je craindre un effet de bord ?

Merci
Laurent
Avatar
Bruno Desthuilliers
moky a écrit :
(snip)

J'ai résolu mon problème en ajoutant des méthodes dans le programme
principal.

J'ai par exemple, dans manip.py, la classe Fichier, à qui je veux
ajouter la méthode "code" pour un certain script.
Je mets ceci dans ledit script :


def codeFichier(self):
print "mon nom est : "+self.chemin



<hs>
Le formattage de chaines, c'est pas mal aussi:

print "mon nom est %s" % self.chemin
</hs>

manip.Fichier.code = codeFichier



Un monkeypatch, donc.

La méthode "chemin" est définie dans la classe manip.Fichier (de base).



Si c'est une méthode, le résultat de l'affichage risque d'être assez
différent de ce que tu penses !-)

Hint : les parenthèses ne sont pas facultatives pour un appel de
fonction ou de méthode.

Avec ça, ça fonctionne très bien.
Dois-je craindre un effet de bord ?



Essentiellement qu'un autre module écrase manip.Fichier.code.

Le problème majeur avec cette solution, c'est surtout la lisibilité /
maintenabilité.

En général, le monkeypatch est à considérer comme un "dernier recours",
quand tu n'a pas la main sur le code originel (ou ne veut pas maintenir
un fork, ou que le besoin est extrêment ponctuel - script "one-shot" par
exemple - et ne justifie pas une modif du code originel, etc...).

Accessoirement, la syntaxe "objet.methode(arg)" est essentiellement un
sucre syntaxique pour "methode(objet, arg)", et Python ne t'oblige en
rien à mettre tout ton code dans des classes. En bref, si codeFichier
n'est utilisé que localement et que tu n'a pas besoin de polymorphisme,
*la* solution évidente et pythonesque est d'utiliser une
BonneVieilleFonction(tm) - donc de supprimer le monkeypatch et d'appeller :

codeFichier(unRepertoire)

plutôt que :

unRepertoire.codeFichier()


mes 2 centimes...
Avatar
moky
En bref, si codeFichier
n'est utilisé que localement et que tu n'a pas besoin de polymorphisme,
*la* solution évidente et pythonesque est d'utiliser une
BonneVieilleFonction(tm) - donc de supprimer le monkeypatch et d'appeller :

codeFichier(unRepertoire)

plutôt que :

unRepertoire.codeFichier()



ah ah ! C'est ce que je me retenais de faire parce que j'avais dans
l'idée, justement, qu'il était mieux de tout mettre dans les classes.
Mais si je peux faire ça tout en restant pythonesque, alors c'est
évidement ça que je vais faire.

Merci beaucoup pour le coup de main
Laurent
Avatar
Bruno Desthuilliers
moky a écrit :
En bref, si codeFichier
n'est utilisé que localement et que tu n'a pas besoin de
polymorphisme, *la* solution évidente et pythonesque est d'utiliser
une BonneVieilleFonction(tm) - donc de supprimer le monkeypatch et
d'appeller :

codeFichier(unRepertoire)

plutôt que :

unRepertoire.codeFichier()



ah ah ! C'est ce que je me retenais de faire parce que j'avais dans
l'idée, justement, qu'il était mieux de tout mettre dans les classes.



Encore une victime de la pensée unique !-)
1 2