3 votes

Remplacement de __repr__ après la création de l'instance

Je joue un peu avec la métaprogrammation Python.

class FormMetaClass(type):

    def __new__(cls, clsname, bases, methods):
        # Attach attribute names to the descriptors
        for key, value in methods.items():
            if isinstance(value, FieldDescriptor):
                value.name = key
        return type.__new__(cls, clsname, bases, methods)

class Form(metaclass=FormMetaClass):

    @classmethod
    def from_json(cls, incoming):
        instance = cls()
        data = json.loads(incoming)
        for k, v in data.items():
            if (not hasattr(instance, k)):
                raise KeyError("Atrribute not found")
            instance.__setattr__(k, v)
        return cls

class MyForm(Form):

    first_name = String()
    last_name = String()
    age = Integer()

    def __repr__(self):
        return "{} {}".format(self.first_name, self.last_name)

def main():
    data = json.dumps({'first_name': 'Thomas',
                       'last_name': 'Junk'})
    form = MyForm.from_json(data)
    print(form)

if __name__ == "__main__":
    main()

class FieldDescriptor:

    def __init__(self, name=None, **opts):
        self.name = name
        for key, value in opts.items():
            setattr(self, key, value)

    def __set__(self, instance, value):
        instance.__dict__[self.name] = value

class Typechecked(FieldDescriptor):
    expected_type = type(None)

    def __set__(self, instance, value):
        if not isinstance(value, self.expected_type):
            raise TypeError('expected ' + str(self.expected_type))
        super().__set__(instance, value)

class Integer(Typechecked):
    expected_type = int

class String(Typechecked):
    expected_type = str

J'ai un Form qui possède une métaclasse FormMetaClass . Pour disposer d'un constructeur alternatif, j'utilise un constructeur de type @classmethod . Je crée un instance qui semblent fonctionner jusqu'à présent.

Ce qui ne fonctionne pas, c'est l'appel à la fonction __repr__ (ou __str__ de manière interchangeable). Lorsque je crée une instance via MyForm() tout va bien. Lorsque je crée une instance via la fonction @classmethod une mise en œuvre "par défaut" est adoptée.

Je m'attendais à Thomas Junk mais j'obtiens <class '__main__.MyForm'>

Pourriez-vous me donner un indice sur ce que je néglige ?

3voto

Martijn Pieters Points 271458

Vous renvoyez la classe, et non l'instance nouvellement créée :

return cls

Vous revenez donc MyForm et non la nouvelle instance MyForm() il suffit d'activer tous les attributs. Et vous voyez effectivement l'attribut repr() pour la classe :

>>> form is MyForm
True
>>> print(MyForm)
<class '__main__.MyForm'>

La solution est simple : renvoyer instance au lieu de cela :

return instance

ou en tant que méthode complète :

@classmethod
def from_json(cls, incoming):
    instance = cls()
    data = json.loads(incoming)
    for k, v in data.items():
        if (not hasattr(instance, k)):
            raise KeyError("Atrribute not found")
        instance.__setattr__(k, v)
    return instance

la méthode renvoie alors une instance et tout fonctionne :

>>> isinstance(form, MyForm)
True
>>> print(form)
Thomas Junk

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X