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 :

Décorateur automatisé d'__init__, et metaclass


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2012
    Messages
    48
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2012
    Messages : 48
    Par défaut Décorateur automatisé d'__init__, et metaclass
    Bonjour.

    Tout d'abord, le code :
    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
     
    class BaseCreature(object):
        # C'est ici que je coince...
        def __init__(self, pv=1):
            self.pv = pv
     
     
    class Monstre(BaseCreature):
        def __init__(self, laideur=0):
            self.laideur = laideur
     
     
    class Heros(BaseCreature):
        def __init__(self, beaute=0):
            self.beaute = beaute
     
    def test():
        orc = Monstre(pv=4, laideur=3)
        belle_mere = Monstre(laideur=10)
        jean = Heros(beaute=5)
        sekigo = Heros(pv=100, beaute=100)
     
        assert getattr(sekigo, 'pv') == 100
        assert getattr(sekigo, 'beaute') == 100
        assert getattr(belle_mere, 'pv') == 1
        assert getattr(belle_mere, 'laideur') == 10
     
    if __name__ == "__main__":
        test()
    Bon, j'essaye de me "former" à l'utilisation des tests. Mais là n'est pas la question.

    Ce que je voudrais, c'est que des instances de classes soient déjà peuplés de la variable pv SANS avoir à écrire un truc comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class Cailloux(BaseCreature):
        def __init__(self, pv=1, beaute=1):
            super(Cailloux, self).__init__(pv)
            self.beaute = beaute
    mais rester dans le style des classes Heros et Monstre.

    Ceci dit, ce n'est peut-être pas super correct de faire un truc comme cela. Je n'en sais rien. Mais en tout cas, ce serait vraiment plus rapide à écrire.

    En espérant que vous compreniez au moins le problème (je ne suis pas toujours bien clair dans les explications de mes problèmes).
    Et merci d'avoir déjà lu le sujet

  2. #2
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Je pense que je n'ai pas trop compris mais cela ressemble à l'utilisation des variables de classe.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class BaseCreature(object):
        pv = 1
        beaute = 1
        def __init__(self):
            pass
     
    class Cailloux(BaseCreature):
        def __init__(self, pv=2, beaute=0):
            self.beaute = beaute
            print(self.pv)
            print(self.beaute)
     
    c = Cailloux()

  3. #3
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    En fait, je ne comprends pas bien ton problème, moi non plus…

    D’un, pourquoi tu ne veux pas appeler le “constructeur” de la classe parent*? C’est la procédure standard…

    De deux, si ton initialisation est aussi simple que ça, tu peux faire self.pv = 3.14 dans l’init de Monstre & Cie (ça aura de toute façon le même résultat que d’appeler le constructeur de BaseCreature*: self aura un membre pv…).

    De toute manière, l’héritage en python présente surtout un intérêt du côté des fonctions…

    De trois, je vais te dénoncer à la SPBM, moi*!

  4. #4
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 738
    Par défaut
    Salut,

    Vous pouvez utilisez __new__ ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class BaseCreature(object):
        def __new__(cls, pv=1, *args, **kwds):
            obj = object.__new__(cls)
            setattr(obj, 'pv', pv)
            return obj
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2012
    Messages
    48
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2012
    Messages : 48
    Par défaut
    Hop, j'ai réussi à résoudre mon problème, en me basant sur un tutoriel pour apprendre Python et surtout, sur les sources Django.

    Bon, avec le code de solution en bas, ce sera plus simple à comprendre.
    Ce que je voulais, c'était avoir une étape intermédiaire entre la déclaration d'appel à la classe (pas sûr du terme, en gros, l'endroit où l'on dit que l'on veut une instance de la classe Machin) et l'initialisation de l'instance. Ceci, pour "nettoyer" les arguments passé.

    Le code :
    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
     
    class BaseCreature(type):
        def __new__(metacls, name, bases, dct):
            # Petit contrôle des arguments.
            def _initialize(method):
                def _control_arguments(self, *args, **kwargs):
                    pv = kwargs.pop('pv', 1)
                    self.pv = pv
                    return method(self, *args, **kwargs)
                return _control_arguments
     
            if dct.get('__init__', None):
                dct['__init__'] = _initialize(dct['__init__'])
            return type.__new__(metacls, name, bases, dct)
     
    class Creature(object):
        __metaclass__ = BaseCreature
     
     
     
    class Monstre(Creature):
        def __init__(self, laideur=0):
            self.laideur = laideur
     
    class Heros(Creature):
        def __init__(self, beaute=0):
            self.beaute = beaute
     
     
    def test():    
        orc = Monstre(pv=4, laideur=3)
        belle_mere = Monstre(laideur=10)
        jean = Heros(beaute=5)
        sekigo = Heros(pv=100, beaute=100)
     
        assert getattr(sekigo, 'pv') == 100
        assert getattr(sekigo, 'beaute') == 100
        assert getattr(belle_mere, 'pv') == 1
        assert getattr(belle_mere, 'laideur') == 10
     
     
    if __name__ == "__main__":
        test()
    Je ne pouvais pas utiliser les variables de classes. Je veux des variables propre à chaque instance crée avec la classe, je ne veux pas d'une variable de l'instance de classe.
    Et je ne pouvais pas faire un self.pv = 32 dans chaque init. Parce que si j'ai 30 classes qui dérive de la classe Creature, ça va vite me saouler.
    Évidemment, je peux appeler le constructeur de la classe parente. Mais si je modifie pour X ou Y raison un truc dans les arguments de la classe parente, je dois les faire répercuter sur toutes les classes filles.

    Bon, là, j'ai utiliser les metaclass, surtout parce que ça faisait un moment que je voulais commencer à apprendre à m'en servir. Mais je pense qu'on doit pouvoir le faire dans le __new__ de la classe Creature.
    Surtout que j'ai lu à de nombreuses reprises sur le net que les metaclass, dans 99% des cas où l'on pense en avoir besoin, c'est que l'on en a pas besoin...

    Merci de vos réponses ! J'éditerais quand j'aurais le code avec le __new__

  6. #6
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 738
    Par défaut
    Salut,
    la metaclass 'decore' __init__. C'est ce qui permet de filtrer la liste des arguments passés à l'__init__ "original".
    Cela 'automatise' la construction du code suivant:
    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
    class Creature(object): pass
     
    def initialize(**default):
        def inner_fct(f):
            def wrapper(self, *args, **kwds):
                for k, v in default.iteritems():
                    setattr(self, k, kwds.pop(k, v))
                f(self, *args, **kwds)
            return wrapper
        return inner_fct
     
    class Monstre(Creature):
        @initialize(pv=1)
        def __init__(self, laideur=0):
            self.laideur = laideur
     
    class Heros(Creature):
        @initialize(pv=1)
        def __init__(self, beaute=0):
            self.beaute = beaute
    Mais en fait, ce qu'on voudrait peut être c'est:
    1. Définir une hiérarchie de classe qui permette de définir attributs et valeurs par défaut,
    2. Prendre en compte la liste d'arguments passé au constructeur pour modifier ces défauts.

    En gros l'interface de la chose pourrait être:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Creature(object):
        __metaclass__ = _base_
        pv = field(int, 1)
     
    class Monstre(Creature): 
        laideur = field(int, 0)
     
    class Heros(Creature):
        beaute = field (int, 0)
    field est un descripteur défini ainsi:
    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
    class field(object):
        def __init__(self, factory, default=None):
            self._factory = factory
            self._default = default
            self.name = None
     
        def __get__(self, instance, klass=None):
            assert self.name
            if hasattr(instance, '_%s' % self.name):
                return getattr(instance, '_%s' % self.name)
            return self._default
     
        def __set__(self, instance, args):
            assert self.name
            v = self._factory(args)
            setattr(instance, '_%s' % self.name, v)
    Dans la metaclass, on doit faire 2 choses:
    • initialiser les descriptors,
    • initialiser les instances.


    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
    class _base_(type):
     
        def __new__(mcs, name, bases, dct):
            for k, v in dct.iteritems():
                if isinstance(v, field):
                    v.name = k
            return type.__new__(mcs, name, bases, dct)
     
        def __call__(cls, **kwds):
            obj = object.__new__(cls)
            for c in cls.__mro__:
                for k, v in c.__dict__.iteritems():
                    if isinstance(v, field):
                        setattr(obj, k, v._default)
            for k, v in kwds.items():
                if hasattr(obj, k):
                    setattr(obj, k, v)
            return obj
    Il y a des constructions qui ne me plaisent pas, mais les idées sont là et "çà fonctionne".

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

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

Discussions similaires

  1. envoi automatisé de mails à partir d'une base postgresql
    Par youn608 dans le forum PostgreSQL
    Réponses: 11
    Dernier message: 15/02/2005, 09h06
  2. backup automatisé
    Par Pierre63 dans le forum Administration
    Réponses: 6
    Dernier message: 24/01/2005, 09h07
  3. Export automatisé de données
    Par LeLaid dans le forum Access
    Réponses: 6
    Dernier message: 26/11/2004, 09h02
  4. Réponses: 2
    Dernier message: 04/10/2004, 14h30
  5. Traitement automatisé journalier
    Par regbegpower dans le forum SQL Procédural
    Réponses: 6
    Dernier message: 21/01/2004, 09h51

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