2010-06-02 15 views
17

Ich weiß, dass ich dynamisch so etwas wie, indem Sie eine Instanz-Methode zu einem Objekt hinzufügen:Dynamisches Hinzufügen @property in 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) 

Später ich anrufen kann instance.my_method() und selbst korrekt gebunden sein und alles funktioniert.

Jetzt meine Frage: Wie genau das Gleiche zu tun, um das Verhalten zu erhalten, dass die neue Methode mit @property dekorieren würde geben?

würde ich so etwas wie erraten:

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

Aber tun, dass instance.my_method eine Eigenschaft Objekt zurückgibt.

+0

@Bo Persson: Obwohl der Titel der Frage _python: Wie Eigenschaft dynamisch zu einer Klasse hinzufügen? _ Ist im Grunde das gleiche, ich glaube nicht, dass seine angenommene Antwort (einschließlich Add-on) die Frage anspricht allgemein. – martineau

+0

@martineau - Niemand sonst schien das auch zu glauben, also ist die enge Abstimmung längst abgelaufen. –

+0

@BoPersson: Ich stehe korrigiert, technisch die Info in der [added-on-Antwort] (http://stackoverflow.com/a/1355444/355230) auf diese Frage behandelt die allgemeine Frage. – martineau

Antwort

32

Die property Descriptor Objekte muss in der Klasse, nicht in Instanz, leben, um die Wirkung, die Sie wünschen zu haben. Wenn Sie möchten die bestehende Klasse nicht verändern, um zu vermeiden, das Verhalten anderer Instanzen zu ändern, müssen Sie eine „pro-Instanzklasse“ machen, zB:

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)) 

Ich Kennzeichnung dieses spezielle "per-instance" -Klassen mit einem Attribut, das es unnötig macht, mehrere Einsen zu erstellen, wenn Sie mehrere addprop Aufrufe für dieselbe Instanz ausführen.

Bitte beachten, dass, wie für andere Verwendungen von property, können Sie die Klasse im Spiel brauchen neuer Stil (in der Regel durch Erben direkt oder indirekt aus object) erhalten werden, nicht der alten Legacy-Stil (fiel in Python 3) das standardmäßig einer Klasse ohne Basen zugewiesen wird.

+12

Ich folgte nur einem Link zu dieser Antwort und fand es sehr nützlich. Allerdings, vielleicht aufgrund von Änderungen in Python in den letzten Jahren, ein paar Dinge haben nicht funktioniert out of the box für mich. Ich musste 'cls.hasattr ('__ perinstance')' in 'hasattr (cls, '__perinstance')' ändern, und ich musste eine Zeile innerhalb des 'if' Blocks hinzufügen:' inst .__ class__ = cls'. Ich hoffe, das hilft jedem anderen, der diese Antwort sieht, damit es für sie funktioniert! – Blckknght

+8

@Blckknght: Ich sehe nicht, wie die ursprüngliche Antwort funktioniert haben könnte, weil sie niemals die Klasse der Instanz in die neu erstellte "pro Instanz" ändert, wenn das auftritt. – martineau

3

Da diese Frage zu einer spesific Instanz Zugabe nicht über nur fragen, das folgende Verfahren verwendet werden können, eine Eigenschaft der Klasse hinzuzufügen, wird dies die Eigenschaften für alle Instanzen der Klasse YMMV aussetzen.

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

Hinweis: Hinzufügen eine weitere Antwort, weil ich @ Alex Martelli denken, während korrekt ist, wird das gewünschte Ergebnis zu erzielen, indem eine neue Klasse zu erstellen, die die Eigenschaft besitzt, wird diese Antwort soll direktere/unkompliziert sein, ohne abstrahieren, was in seiner eigenen Methode vor sich geht.

+0

-1, dies betrifft nicht nur das Verhalten von 'my_instance', es setzt auch' my_prop' für alle anderen Instanzen der Klasse 'type (my_instance)' –

+0

@ali_m, Richtig, das ist der Zweck, das Hinzufügen des Attributs zum Klasse, Die Antwort oben (die als die richtige überprüft wird). fügt der Klasse auch die Eigenschaft hinzu (nicht die Instanz). – ideasman42

+1

Nein, was passiert ist, dass 'cls = type (cls .__ name__, (cls,), {})' eine neue Instanz pro Instanz erzeugt, deren Basis der Typ der Instanz ist, der wir das Attribut hinzufügen . Wenn Sie dann inst.__ class__ = cls wie in Blckknghts Kommentar zuweisen, wird das Attribut nur auf inst angewendet, nicht auf alle Instanzen der Klasse type (inst). –

Verwandte Themen