2009-10-30 12 views
12

Nie znam Pythona (nigdy wcześniej go nie używał: D), ale nie mogę znaleźć niczego w Internecie. Może po prostu nie zadałem poprawnego pytania, ale oto:Czy można zmienić implementację metody instancji bez zmiany wszystkich innych instancji tej samej klasy?

Chcę zmienić implementację konkretnej metody instancji. Kiedy google za to, znalazłem można to zrobić, ale zmienia realizację wszystkich innych przypadkach z tej samej klasy, na przykład:

def showyImp(self): 
    print self.y 

class Foo: 
    def __init__(self): 
     self.x = "x = 25" 
     self.y = "y = 4" 

    def showx(self): 
     print self.x 

    def showy(self): 
     print "y = woohoo" 

class Bar: 
    def __init__(self): 
     Foo.showy = showyImp 
     self.foo = Foo() 

    def show(self): 
     self.foo.showx() 
     self.foo.showy() 

if __name__ == '__main__': 
    b = Bar() 
    b.show() 
    f = Foo() 
    f.showx() 
    f.showy() 

To nie działa zgodnie z oczekiwaniami, ponieważ wynik jest następujący :

x = 25

y = 4

x = 25

y = 4

I ma to być:

x = 25

y = 4

x = 25

y = woohoo

I próbowałem zmienić metodę init Bar'a w następujący sposób:

def __init__(self): 
    self.foo = Foo() 
    self.foo.showy = showyImp 

Ale pojawia się następujący komunikat o błędzie:

showyImp() wykonuje dokładnie 1 argument (0 podany)

Więc tak ... Próbowałem za pomocą setattr(), ale wydaje jak to jest takie samo jak self.foo.showy = showyImp.

Jakaś wskazówka? :)

+2

Czy istnieje szczególny powód, dla którego nie robisz tego z dziedziczeniem? –

+2

Tak, ponieważ mam bardzo ograniczony dostęp do utworzonego obiektu.Nie jestem tym, który ją tworzy (stąd brak możliwości dziedziczenia), i nie mogę modyfikować kodu oryginalnego obiektu (również dlatego, że i tak nie miałoby to sensu, muszę zmienić metodę tylko w kontekście, który jest zupełnie inny z czego obiekt jest przeznaczony na :). –

Odpowiedz

17

Wszystko co chcielibyście wiedzieć o Python Attributes and Methods.

Tak, jest to odpowiedź pośrednia, ale demonstruje szereg technik i wyjaśnia niektóre z bardziej skomplikowanych szczegółów i "magii".

Aby uzyskać odpowiedź "bardziej bezpośrednią", rozważ python's new module. W szczególności spójrz na funkcję instancemethod, która pozwala "związać" metodę z instancją - w tym przypadku pozwoli ci to na użycie "self" w metodzie.

import new 
class Z(object): 
    pass 
z = Z() 
def method(self): 
    return self 
z.q = new.instancemethod(method, z, None) 
z is z.q() # true 
+0

Wielkie dzięki, dokładnie to, czego szukałem i działa jak czar :) –

4

Jeśli wiązanie z instancji, nie powinny zawierać argument samodzielne:

>>> class foo(object): 
...  pass 
... 
>>> def donothing(): 
...  pass 
... 
>>> f = foo() 
>>> f.x = donothing 
>>> f.x() 
>>> 

Trzeba argument samodzielne jeśli wiązanie do klasy choć:

>>> def class_donothing(self): 
...  pass 
... 
>>> foo.y = class_donothing 
>>> f.y() 
>>> 
+2

Muszę powiązać instancję i potrzebuję dostępu do "ja" i nie mogę zmodyfikować istniejącej procedury wywołania metody, dlatego nie mogę wykonać 'self.foo.showy (self.foo)' –

0

Twój przykład jest trochę poskręcany i skomplikowany, i nie bardzo rozumiem, co to ma wspólnego z twoim pytaniem. Jeśli chcesz, możesz to wyjaśnić.

Jest jednak całkiem łatwo zrobić to, co chcesz zrobić, zakładając, że dobrze czytam twoje pytanie.

class Foo(object): 
    def bar(self): 
     print('bar') 

def baz(): 
    print('baz') 

W tłumaczu ...

>>> f = Foo() 
>>> f.bar() 
bar 
>>> f.bar = baz 
>>> f.bar() 
baz 
>>> g = Foo() 
>>> g.bar() 
bar 
>>> f.bar() 
baz 
+1

Problem z tym rozwiązaniem polega na tym, że nie używa on 'self' (jako parametru), którego potrzebuję i nie mogę zrobić f.bar (f), głównie dlatego, że nie chcę (wygląda bardzo podobny do wzoru). –

+0

Ach, rozumiem. – hanksims

5

Jeśli kiedykolwiek trzeba zrobić to dla specjalnej metody (które dla klasy nowego stylu - czyli co należy zawsze używać i jedyny rodzaj w Pythonie 3 - to wyglądało się na klasy, a nie instancji), można po prostu zrobić klasę per-instancji, np ...:

self.foo = Foo() 
meths = {'__str__': lambda self: 'peekaboo!'} 
self.foo.__class__ = type('yFoo', (Foo,), meths) 

Edit: Zostałem poproszony o wyjaśnienie zalety tego podejścia wrt nowego .instancemethod ...:

>>> class X(object): 
... def __str__(self): return 'baah' 
... 
>>> x=X() 
>>> y=X() 
>>> print x, y 
baah baah 
>>> x.__str__ = new.instancemethod(lambda self: 'boo!', x) 
>>> print x, y 
baah baah 

Jak widać, new.instancemethod jest całkowicie bezużyteczny w tym przypadku. OTOH ...:

>>> x.__class__=type('X',(X,),{'__str__':lambda self:'boo!'}) 
>>> print x, y 
boo! baah 

... Przypisanie nowej klasy działa świetnie dla tej sprawy i dla wszystkich innych. BTW, jak mam nadzieję, jest jasne, kiedy już zrobisz to dla danej instancji, możesz później dodać więcej metod i innych atrybutów klasy do jej x.__class__ i wpływać samoistnie tylko na jedną instancję!

+0

Jaka byłaby przewaga tej metody w porównaniu do używania 'nowego. instancemethod'? –

+0

To ostatnie podejście nie działa na specjalnych metodach w klasach nowego stylu, jak już powiedziałem, podczas gdy ten jest ogólny. Pozwól mi edytować odpowiedź, aby pokazać przykład. –

+0

Ah ok, rozumiem! Dziękuję bardzo za informacje. To interesujące :). –

0

Nie rób tego.

Zmiana metod jednej instancji jest niepoprawna.

Oto zasady projektowania OO.

  1. Unikaj magii.

  2. Jeśli nie możesz korzystać z dziedziczenia, użyj delegacji.

Oznacza to, że za każdym razem, gdy myślisz, że potrzebujesz czegoś magicznego, powinieneś pisać "oblewanie" lub elewację wokół obiektu, aby dodać pożądane funkcje.

Wystarczy napisać opakowanie.

+0

Mój komentarz był zbyt długi dla SO, więc tutaj jest pastie: http://pastie.org/677619 :) –

+0

@naixn: To jest twoje pytanie. Jesteś właścicielem pytania. Możesz zaktualizować pytanie. Jeśli zaktualizujesz pytanie tak, aby zawierał WSZYSTKIE fakty, nie musisz uciekać się do wiadomości pozapasmowych. Proszę zaktualizować pytanie za pomocą szczegółowego uzasadnienia dla zasadniczo wadliwego projektu. Twoje wyjaśnienie to "Nie chcę owijać i delegować". (1) Proszę ZAKTUALIZUJ swoje pytanie, (2) Ponownie rozważ swój wadliwy projekt. –

+0

Co zrobić, jeśli potrzebujesz biblioteki innej firmy, aby zachować się nieco inaczej? W moim konkretnym przypadku potrzebuję jednego konkretnego wystąpienia 'Logger', aby dołączyć trochę danych do każdego komunikatu dziennika, który biblioteka wysyła do metody' info() 'programu rejestrującego. Czy można w tym celu użyć opcji wrap-and-delegate? – CoreDumpError

11

Od Pythona 2.6, należy użyć modułu typesMethodType Klasa:

from types import MethodType 

class A(object): 
    def m(self): 
     print 'aaa' 

a = A() 

def new_m(self): 
    print 'bbb' 

a.m = MethodType(new_m, a) 

Jako inna odpowiedź wskazał jednak, to nie będzie działać dla metody „magicznych” klas w nowym stylu, takich jak __str__() .

+0

Dziękujemy! Przyjęta odpowiedź nie działa w języku Python 3, a strona dokumentacji nie wyjaśnia, w jaki sposób korzystać z "MethodType". – CoreDumpError

Powiązane problemy