2012-06-14 13 views
6

Mam kilka różnych obiektów (różne nazwy funkcji, różne podpisy) i małpkę łatam je, aby mieć wspólny sposób dostępu do nich z różnych funkcji. Krótko mówiąc, istnieje dyspozytor, który bierze obiekty, które chcę łatać i w zależności od typu obiektu, który wywołuje inny patcher. Patcher doda metody obiektu:Dodawanie klasy bazowej do istniejącego obiektu w pythonie

def patcher_of_some_type(object): 

    def target(self, value): 
     # do something and call self methods 

    object.target = types.MethodType(target, object) 

    # many more of these 

Ponieważ program staje się coraz bardziej skomplikowane, co owijkę wokół obiektu (lub klasy obiektów) wydaje się być lepszym pomysłem. Niektóre łatki mają wspólny kod lub są ze sobą powiązane. Ale nie kontroluję tworzenia obiektu ani tworzenia klasy. Dostaję tylko przedmioty. I nawet gdybym mógł to zrobić, po prostu chciałbym owijać (lub łatać) pewne obiekty, nie wszystkie.

Jednym z rozwiązań może być dodanie klasy bazowej do istniejącego obiektu, ale nie jestem pewien, jak to jest możliwe do utrzymania i bezpieczne. Czy istnieje inne rozwiązanie?

+0

Tak zajęcia nie są zdefiniowane od ciebie? – dav1d

+0

Nie, klasy są zdefiniowane w bibliotece, której nie kontroluję. Mogłabym tworzyć wrappery ze wszystkich klas, ale to będzie działać tylko dla obiektów, które stworzyłem, a nie dla tych, które są tworzone przez bibliotekę. Jeśli poprawię bibliotekę, nastąpi odwrotność: wszystkie obiekty są załatane, a ja chcę tylko załatać niektóre z nich. Wreszcie, jeśli zawijam niektóre obiekty, potrzebowałbym zachować własną bazę danych, aby się do nich odnieść. Właśnie dlatego teraz łatam obiekty, które chcę. – Hernan

+1

Zdefiniowałbym klasę otoki do przechowywania obiektów. Potrzeba posiadania różnych rodzajów owijki, które mają wspólny kod, może być obsługiwana przez posiadanie podstawowej klasy opakowania i wyprowadzanie z niej specjalistycznych wersji. Podstawowe rzeczy z OOP ... – martineau

Odpowiedz

9

Dynamiczne zmiany typu obiektu jest stosunkowo bezpieczne, jak o ile dodatkowa klasa bazowa jest kompatybilna (a otrzymasz wyjątek, jeśli nie jest). Najprostszym sposobem, aby dodać klasę bazową jest z 3-argumentu type konstruktora:

cls = object.__class__ 
object.__class__ = cls.__class__(cls.__name__ + "WithExtraBase", (cls, ExtraBase), {}) 
+1

przy użyciu typu (i użyj po prostu 'type' zamiast' object .__ class __.__ class__', który btw nie działa z klasami starego stylu!) Jest niezbędny tylko wtedy, gdy musisz ustawić nazwę klasy, w przeciwnym razie możesz po prostu zrobić 'class AnyNameHere (cls, ExtraBase): pass', który jest bardziej czytelny. – l4mpi

0

Aby dodać klasę do kolejnych baz klas, można zrobić coś takiego:

def pacher_of_some_type(object): 

    #this class holds all the stuff you want to add to the object 
    class Patch(): 
     def target(self, value): 
      #do something 

    #add it to the class' base classes 
    object.__class__.__bases__ += (Patch,) 

Ale to będzie dotyczyć wszystkich obiektów tej klasy, jak faktycznie zmienić samej klasy, co nie wydaje się to, co chcesz ... jeśli chcesz tylko załatać instancji można podklasy oryginalnej klasy i zmienić klasę instancji dynamicznie:

class PatchedClass(object.__class__): 
    def target(self, value): 
     #do whatever 

object.__class__ = PatchedClass 

Pod względem bezpieczeństwa i łatwości zarządzania, powiedziałbym dynamicznie dodanie klasa bazowa jest tak samo łatwa do obsługi, jak dynamiczne dodawanie niektórych funkcji ns i bezpieczniejsze, ponieważ mniej prawdopodobne jest przypadkowe nadpisanie niektórych wewnętrznych funkcji oryginalnej klasy, ponieważ nowa klasa bazowa jest dodawana na końcu krotki, co oznacza, że ​​jest używana jako ostatnia do rozpoznawania nazw. Jeśli rzeczywiście chcesz nadpisać metody, dodaj je na początku tuple klas bazowych (oczywiście nie dotyczy to podejścia podklasy). Nie pytaj mnie, czy dynamicznie zmieniających klasę instancji jest bezpieczny w ogóle, ale w trywialny przykład nie wydaje się być problemem:

class A(): 
    def p(self): print 1 

class B(): 
    def p(self): print 2 

a = A() 
a.p() #prints 1 
a.__class__ = B 
a.p() #prints 2 
2

oparta na comment na innej odpowiedzi, jest to kolejny sposób załatać klasę bazową:

class Patched(instance.__class__, ExtraBase): 
    pass 

instance.__class__ = Patched 
+1

To jest naprawdę miłe! Wygląda jednak na to, że zmieniła nazwę klasy na Patched. Możesz obejść to poprzez dodanie linii: 'Patched .__ name__ = instance .__ class __.__ name__'. – paulmelnikow

Powiązane problemy