2010-06-02 10 views
17

wiem, że można dynamicznie dodać metodę instancji do obiektu robiąc coś takiego:Dynamicznie dodając @property w Pythonie

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

Później mogę zadzwonić instance.my_method() i samo będzie prawidłowo związane i wszystko działa.

Teraz, moje pytanie: jak zrobić dokładnie to samo, aby uzyskać zachowanie, które dałoby dekorowanie nowej metody za pomocą @property?

Przypuszczam coś takiego:

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

Ale robi to instance.my_method zwraca obiekt nieruchomości.

+0

@Bo Persson: Mimo że tytuł pytania _python: Jak dynamicznie dodawać własność do klasy? _ Jest zasadniczo taki sam, nie wierzę, że jego akceptowana odpowiedź (w tym dodatek do niej) rozwiązuje problem ogólnie. – martineau

+0

@martineau - Nikt inny też w to nie wierzył, więc bliskie głosowanie wygasło dawno temu. –

+0

@BoPersson: Poprawiam się, technicznie informacje z [dodanej odpowiedzi] (http://stackoverflow.com/a/1355444/355230) na to pytanie są odpowiedzią na ogólne pytanie. – martineau

Odpowiedz

32

W property obiekty deskryptora musi żyć w klasie , nie w instancji, aby mieć wpływ pragnienie. Jeśli nie chcesz zmieniać istniejącą klasę w celu uniknięcia zmiany zachowania innych przypadkach trzeba zrobić „per-instancji klasy”, np:

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

mam oznakowania te specjalne klasy "na instancję" z atrybutem pozwalającym uniknąć niepotrzebnego robienia wielu, jeśli wykonujesz kilka wywołań addprop w tej samej instancji.

Należy zauważyć, że, podobnie jak w przypadku innych zastosowań property, trzeba klasę w grze będzie nowy styl (zazwyczaj otrzymuje się przez dziedziczenie bezpośrednio lub pośrednio od object) nie starożytne dziedzictwo styl (spadł w Pythonie 3), który jest domyślnie przypisany do klasy bez podstaw.

+12

Po prostu podążyłem za linkiem do tej odpowiedzi i okazało się, że jest to bardzo przydatne. Być może jednak ze względu na zmiany w Pythonie w ciągu ostatnich kilku lat, kilka rzeczy nie wyszło mi z pudełka. Musiałem zmienić 'cls.hasattr ('__ perinstance')' na 'hasattr (cls, '__perinstance')' i musiałem dodać wiersz w bloku 'if':' inst .__ class__ = cls'. Mam nadzieję, że pomoże to każdemu, kto dostrzeże tę odpowiedź, aby działało prawidłowo! – Blckknght

+8

@Blckknght: Nie widzę, jak oryginalna odpowiedź mogła zadziałać, ponieważ nie zmienia ona nigdy klasy instancji na nowo utworzoną "na instancję", gdy taka sytuacja wystąpi. – martineau

3

Ponieważ kwestia ta nie jest prośbą o tylko dodanie do Spesific przykład następujące metody mogą być stosowane, aby dodać obiekt do klasy, to narazić właściwości dla wszystkich instancji klasy YMMV.

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

Uwaga: Dodanie kolejnego odpowiedź bo myślę @Alex Martelli, podczas gdy prawidłowa, jest osiągnięcie pożądanego rezultatu, tworząc nową klasę, która posiada właściwość, odpowiedź ta ma być bardziej bezpośredni/proste bez streszczanie tego, co dzieje się we własnej metodzie.

+0

-1, nie wpływa to tylko na zachowanie 'my_instance', ale także ustawia' my_prop' dla wszystkich innych instancji klasy 'type (my_instance)' –

+0

@ali_m, Prawo, to jest cel, to dodanie atrybutu do klasa, powyższa odpowiedź (która jest zaznaczona jako poprawna). również dodaje właściwość do klasy (nie do instancji). – ideasman42

+1

Nie, co się dzieje, to że 'cls = type (cls .__ name__, (cls,), {})' tworzy nową klasę 'per-instance', której podstawą jest typ instancji, do której dodajemy atrybut . Kiedy następnie przydzielisz 'inst .__ class__ = cls' jak w komentarzu Blckknght, to spowoduje, że atrybut zostanie zastosowany tylko do' inst', a nie do wszystkich instancji klasy 'type (inst)'. –