IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Changer la classe d'appartenance d'un objet


Sujet :

Python

  1. #1
    Membre expérimenté Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Points : 1 481
    Points
    1 481
    Par défaut Changer la classe d'appartenance d'un objet
    Bonjour,

    Avec ce titre, une explication du contexte s'impose !

    J'utilise un package (Shapely pour ceux qui connaissent) qui propose un certain nombre de classes d'objets géométriques : Point, LineString, Polygon , etc.. les noms parlent d'eux-memes.

    Il n'est pas exclu, qu'un jour, je veuille en changer. Du coup, je ne souhaite pas utiliser directement ce package dans mes développements plus "haut niveau" mais passer par une "couche intermédiaire". Je la définirai moi-même et je déciderai des évolutions si besoin. Tout changement du package bas niveau ne se répercutera alors "que" dans cette couche intermédiaire (ça ne bavera pas partout).

    Pour fixer les idées, je pourrais écrire des classes comme :

    - Ponctuel (qui hériterait de Point),
    - Lineaire (qui hériterait de LineString),
    - Surfacique (qui hériterait de Polygon),
    etc...

    avec, peu ou prou, les mêmes méthodes que celles déjà existantes. Mais je coince sur le problème suivant.

    Plusieurs méthodes, dans chacune des classes Point, LineString, Polygon, ..., prennent, en plus de self, un seul argument (une instance quelconque de ces classes) et renvoient une instance d'une de ces classes. Par exemple :

    instance_LineString.intersection(instance_Polygon) peut retourner une instance de Point, de LineString, ..., rien

    Si je dérive "bêtement" :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     
    class Lineaire(LineString):
     
        def intersection(self,other):
     
            res = LineString.intersection(self,other)
     
            if isinstance(res,Point):
                # retourner un Ponctuel et pas un Point !
                return ...
     
            elif isinstance(res,LineString);
                # retourner un Lineaire et pas un LineString !
                return ...
    comment retourner des instances de Ponctuel, Lineaire, Surfacique ... et non pas de Point, LineString, Polygon ?

    Je peux évidemment créer, pour chaque nouvelle classe, un constructeur qui, à partir d'un Point, construirait un Ponctuel, d'un LineString, un Lineaire, etc... et faire l'instanciation (un "clonage") avant de retourner le résultat mais :

    1 - plus les classes sont "compliquées", plus l'instanciation est "lourde"
    2 - j'ai parfois de très grands nombres d'objets

    Bref, ça m'embête de procéder de la sorte, d'autant plus que "logiquement", les instances créées seraient "quasiment identiques" aux originaux.

    J'ai bien essayé la solution "volontariste", en réaffectant brutalement __class__ et les différentes méthodes dans le constructeur, ça semble "marcher" mais je trouve cela un peu "bricolage".

    Auriez-vous des idées ? Faut-il chercher du côté des décorateurs ? des méta-classes ? j'avoue avoir atteint mon niveau d'incompétence !

    ps : petite précision qui a, peut-être, son importance : toutes les classes Point, LineString, Polygon, ... du package de base sont dérivées d'une même classe

  2. #2
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Bonsoir,

    J'expérimente...
    L'idée de départ est de passer par un Adapter, c'est à dire d'enrober les classes du module au lieu d'en hériter. J'utilise __getattr__ afin de convertir (adapter) automatiquement les attributs et les méthodes.
    Ceci est un premier jet qui demande encore du polissage. L'utilisation d'une metaclasse devrait (je l'espère) permettre de créer le dict _wrappers automatiquement ainsi que les constructeurs (__init__):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    # adapter.py
    class Adapter(object):
        @classmethod
        def _wraps(cls, o):
            obj = cls.__new__(cls)
            obj._wrapped = o
            return obj
     
        def __getattr__(self, name):
            return adapt(getattr(self._wrapped, name))
     
        def __str__(self):
            return str(self._wrapped)
     
     
    def adapt(obj):
        cls = obj.__class__
        if cls in _wrappers:
            return _wrappers[cls]._wraps(obj)
        if callable(obj):
            def adapt_call(*args, **kwargs):
                args = (unwraps(arg) for arg in args)
                kwargs = {k:unwraps(v) for k,v in kwargs}
                return adapt(obj(*args, **kwargs))
            return adapt_call
        return obj
     
    def unwraps(obj):
        return obj._wrapped if isinstance(obj, Adapter) else obj
     
     
    import shapely.geometry as geom
     
    class Point(Adapter):
        def __init__(self, *args, **kwargs):
            self._wrapped = geom.Point(*args, **kwargs)
     
    class MultiLine(Adapter):
        def __init__(self, *args, **kwargs):
            self._wrapped = geom.LineString(*args, **kwargs)
     
    _wrappers = {
        geom.Point: Point,
        geom.LineString: MultiLine,
        }
    (Python 2.7 uniquement car j'utilise une dictionary comprehension, mais on pourrait s'en passer)
    Tests:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    >>> import adapter
    >>> l1 = adapter.MultiLine(((0,0),(1,1)))
    >>> l1
    <adapter.MultiLine object at 0x029AD8D0>
    >>> l1._wrapped
    <shapely.geometry.linestring.LineString object at 0x02A0CB70>
    >>> print l1
    LINESTRING (0.0000000000000000 0.0000000000000000, 1.0000000000000000 1.0000000000000000)
    >>> l2 = adapter.MultiLine(((0,1),(1,0)))
    >>> p = l1.intersection(l2)
    >>> p
    <adapter.Point object at 0x02AD5BF0>
    >>> print p
    POINT (0.5000000000000000 0.5000000000000000)

  3. #3
    Membre expérimenté Avatar de plxpy
    Homme Profil pro
    Ingénieur géographe
    Inscrit en
    Janvier 2009
    Messages
    792
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 59
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur géographe
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Janvier 2009
    Messages : 792
    Points : 1 481
    Points
    1 481
    Par défaut
    Oh que ça me plait !

    Je vais regarder tout ça en détail et quand j'en viendrai à bout, je posterai un autre message.

    Merci beaucoup dividee.

  4. #4
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Tout compte fait, cette solution est tout de même fragile; par exemple si une méthode retourne non pas un objet "adapté" mais une structure de données qui contient cet objet (par exemple, une liste de Points), cela ne fonctionnera pas. On peut prévoir les cas simples (objet(s) dans une liste, un tuple, ...) mais pas tous les cas. Le même problème se pose pour les arguments des méthodes (fonction unwraps). Cela n'est donc pas une solution générale.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [2.x] Héritage - Changer la classe de mon objet par sa sous-classe
    Par sly_web2 dans le forum Symfony
    Réponses: 1
    Dernier message: 04/09/2012, 12h08
  2. [PHP 5.2] [POO] Changer la classe d'un objet déjà instancié
    Par fourchette dans le forum Langage
    Réponses: 5
    Dernier message: 06/08/2009, 15h46
  3. Réponses: 2
    Dernier message: 09/08/2005, 09h51
  4. Réponses: 6
    Dernier message: 15/05/2005, 15h11
  5. [VB.NET] Changer de classe selon condition
    Par daner06 dans le forum Windows Forms
    Réponses: 4
    Dernier message: 24/10/2004, 11h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo