2010-06-02 10 views
17

Je sais que je peux ajouter dynamiquement une méthode d'instance à un objet en faisant quelque chose comme:Dynamiquement ajouter @property en python

import types 
def my_method(self): 
    # logic of method 
# ... 
# instance is some instance of some class 
instance.my_method = types.MethodType(my_method, instance) 

Plus tard je peux appeler instance.my_method() et l'auto sera correctement lié et tout fonctionne.

Maintenant, ma question: comment faire exactement la même chose pour obtenir le comportement que la décoration de la nouvelle méthode avec @property donnerait?

Je suppose que quelque chose comme:

instance.my_method = types.MethodType(my_method, instance) 
instance.my_method = property(instance.my_method) 

Mais, en faisant que instance.my_method retourne un objet de propriété.

+0

@Bo Persson: Bien que le titre de la question _python: Comment ajouter dynamiquement une propriété à une classe? _ Est fondamentalement le même, je ne crois pas que sa réponse acceptée (y compris son add-on) aborde la question généralement. – martineau

+0

@martineau - Personne d'autre ne semblait le croire non plus, alors le vote serré a expiré il y a longtemps. –

+0

@BoPersson: Je suis corrigé, techniquement l'information dans [la réponse ajoutée] (http://stackoverflow.com/a/1355444/355230) à cette question répond à la question générale. – martineau

Répondre

32

Les objets de descripteurs property doit vivre dans la classe, pas dans le exemple, d'avoir l'effet que vous désirez. Si vous ne voulez pas modifier la classe existante afin d'éviter de modifier le comportement des autres cas, vous aurez besoin de faire une « instance par classe », par exemple:

def addprop(inst, name, method): 
    cls = type(inst) 
    if not hasattr(cls, '__perinstance'): 
    cls = type(cls.__name__, (cls,), {}) 
    cls.__perinstance = True 
    inst.__class__ = cls 
    setattr(cls, name, property(method)) 

Je marquer ces des classes spéciales "par instance" avec un attribut pour éviter de faire plusieurs appels inutiles si vous faites plusieurs appels addprop sur la même instance.

Notez que, comme pour d'autres utilisations de property, vous avez besoin de la classe en jeu pour être nouveau style (généralement obtenu en héritant directement ou indirectement de object), pas le style ancien héritage (abandonné en Python 3) qui est assigné par défaut à une classe sans bases.

+12

Je viens de suivre un lien vers cette réponse et je l'ai trouvé très utile. Cependant, peut-être en raison des changements de Python au cours des dernières années, quelques choses n'ont pas fonctionné hors de la boîte pour moi. J'ai dû changer 'cls.hasattr ('__ perinstance')' en 'hasattr (cls, '__perinstance')', et j'ai dû ajouter une ligne dans le bloc 'if':' inst .__ class__ = cls'. J'espère que cela aide quelqu'un d'autre qui voit cette réponse à le faire fonctionner pour eux! – Blckknght

+8

@Blckknght: Je ne vois pas comment la réponse originale aurait pu fonctionner car elle ne change jamais la classe de l'instance en une instance "par instance" nouvellement créée quand cela se produit. – martineau

3

Étant donné que cette question ne demande pas à propos seulement ajouter à une instance spesific, la méthode suivante peut être utilisée pour ajouter une propriété à la classe, cela exposera les propriétés à toutes les instances de la classe YMMV.

cls = type(my_instance) 
cls.my_prop = property(lambda self: "hello world") 
print(my_instance.my_prop) 
# >>> hello world 

Note: Ajouter une autre réponse, parce que je pense Martelli @ Alex, tout juste, est obtenir le résultat souhaité en créant une nouvelle classe qui détient le bien, cette réponse est destinée à être plus direct/direct sans abstraire ce qui se passe dans sa propre méthode.

+0

-1, cela n'affecte pas seulement le comportement de 'my_instance', il définit aussi' my_prop' pour toutes les autres instances de la classe 'type (my_instance)' –

+0

@ali_m, Right, c'est le but, c'est l'ajout de l'attribut à la class, La réponse ci-dessus (qui est vérifiée comme étant la bonne). ajoute également la propriété à la classe (pas l'instance). – ideasman42

+1

Non, ce qui se passe c'est que cls = type (cls .__ nom__, (cls,), {}) 'crée une nouvelle classe 'par instance' dont la base est le type de l'instance à laquelle nous ajoutons l'attribut . Lorsque vous affectez ensuite 'inst .__ class__ = cls' comme dans le commentaire de Blckknght, l'attribut est uniquement appliqué à' inst', pas à toutes les instances de la classe 'type (inst)'. –

Questions connexes