Forum Programmation.python [Java -> Python] Implémentation d'interface "en ligne"

Posté par . Licence CC by-sa
Tags :
2
21
jan.
2015

Bonjour,

J'essaie de traduire en python un bout de code que j'avais écrit en Java et qui pour tout un tas de raisons qu'il serait trop long à expliquer ici, implémente "en ligne" (je ne sais pas si c'est comme ça qu'on dit) une interface. J'utilise python 2.7 et je sais qu'il n'y a pas d'interface parce que héritage multiple, toussa… Mais dans mon code Java l'implémentation de l'interface est stockée dans un attribut (c'est là qu'intervient tout le tas de raisons).

J'ai préparé un petit bout de code pour essayer d'expliciter tout ça :

Mon interface :

public interface Evaluable {
    public double eval(double u);
}

Ma classe principale :

public class Courbe {
    public Evaluable f;

    public Courbe(Evaluable e){
        this.f = e;
    }

    public static void main(String[] args) {
        Courbe c = new Courbe(new Evaluable(){
            public double eval(double u){
                return u*u;
            };
        });

    double resultat = c.f.eval(0.5);
        System.out.println("Résultat = " + resultat);
    }
}

Comme on peut le voir dans le main, c'est dans l'instanciation de la Courbe c que j'implémente l'interface. Et ça, j'arrive pas à le faire en python :-(

Si quelque âme charitable pourrait m'éclairer de ses lumières…

Merci d'avance.

  • # Stout bête

    Posté par (page perso) . Évalué à 6.

    S'il n'y a pas d'interface formelle[*] en Python c'est parce qu'on raisonne en terme de duck-typing, ce n'est pas à cause de la dérivation multiple.

    Ton interface dans l'absolu c'est juste un callable (un truc qui s'appelle comme une fonction). Tu peux faire un truc comme ça:

    class Courbe(object):
        def __init__(self, f):
            self.f = f
    
    def main():
        [...]
        c = Courbe(lambda u: u * u)
        [...]

    Ou bien avec une "vraie" fonction (que je définis bien ici depuis l'intérieur d'une autre fonction, comme toi):

    def main():
        [...]
    
        def foobar(u):
            return u * u
    
        c = Courbe(foobar)
        [...]

    Voire même avec une classe qui définit la méthode __call__ et peut donc être appelée comme une fonction:

    def main():
        [...]
        class Foobar(object):
            def __call__(self, u):
                return u * u
    
        c = Courbe(Foobar())
        [...]

    (évidemment si tu tiens vraiment à ta méthode 'eval' tu peux modifier mon dernier exemple, mais je trouve ça moins pythonique).

    [*] Ce qui n'est plus tout à fait vrai: https://docs.python.org/release/2.7/library/abc.html#module-abc

    • [^] # Re: Stout bête

      Posté par . Évalué à 2.

      Merci pour cette réponse riche d'enseignements. Je pense que je vais apprendre plein de choses en explorant les solutions que tu me proposes. J'espère qu'au moins une d'entre elle sera compatible avec toutes mes contraintes, mais je suis optimiste.

      En tout cas, je loue ta magnificence proverbiale.

      Bonne journée

    • [^] # Re: Stout bête

      Posté par . Évalué à 3.

      Du coup je me pose une question : la manière de faire pour une interface plus complexe (qui a plusieurs méthodes). En java c'est un peu lourd mais ça suit la même syntaxe, en python tu va prendre un objet et lui donner dynamiquement les paramètres qui vont bien ?

      obj.function1 = lambda u: u * u
      obj.function2 = lambda u: u + u

      Si c'est ça c'est vraiment de la prog par prototypage :)

      Par contre je ne sais pas comment créer cet obj. Il n'y a pas (à ma connaissance) de classe maitresse de toutes les autres et instancier une autre classe peu être très artificiel.

      Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Stout bête

        Posté par . Évalué à 2.

        Salut,

        On peut quand même faire joujou avec une "pseudo" interface, en jouant avec les exceptions

        class Evaluable(object):
            def eval(u):
                raise NotImplementedException
        
        class Courbe(Evaluable):
            def eval(u):
                return u*u

        … ou utiliser abc.

      • [^] # Re: Stout bête

        Posté par (page perso) . Évalué à 1.

        Je pense que tu as lu un peu vite, ou que je n'ai pas été assez clair.

        Il se trouve que dans le cas particulier qui nous importe, l'interface Evaluable existe déjà à mon sens dans le langage, et c'est ce que j'appelle un callable (appelable comme une fonction donc). Je lui conseille donc de ne pas traduire le code Java trop bêtement en évitant de définir une nouvelle interface.

        Mais comme je l'indique dans ma dernière phrase, il peut remplacer __call__ par eval (dans ce cas il faudra faire f.eval(1) et plus f(1) évidemment), et de même il pourrait rajouter des méthodes eval2 ou zigouigoui si il veut.

        Ainsi que François l'explique au dessus, en général tu vas définir une classe AbstractMachin ou BaseBidulos, dans laquelle tu vas mettre des fonctions :

        • qui soulèvent une exception NotImplementedError
        • qui fournissent une implémentation de base (comme les Adapter de la lib standard Java si je me souviens bien).

        un peu comme tu peux le faire en C++ et ses classes abstraites.

        Il n'y a pas (à ma connaissance) de classe maitresse de toutes les autres

        o = object()

        Bon après rajouter des méthodes à un objet/classe ça arrive et parfois ça "sauve la vie", mais on le fait assez rarement.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.