2009-10-14 12 views
6

Potrzebuję napisać klasę, która pozwala podklasie ustawić atrybut z nazwą funkcji. Ta funkcja musi być następnie wywoływana z instancji klasy.Pisanie klasy, która akceptuje wywołanie zwrotne w Pythonie?

Na przykład mówię, że muszę napisać klasę owoców, w której podklasa może przekazać wiadomość powitalną. Klasa Fruit musi wystawiać atrybut print_callback, który można ustawić.

class Fruit(object): 
    print_callback = None 

    def __init__(self, *args, **kwargs): 
     super(Fruit, self).__init__(*args, **kwargs) 
     self.print_callback("Message from Fruit: ") 

muszę wystawiać API, który może być spożywany przez tego kodu (być jasne, ten kod nie może zmienić, powiedzieć, że jest kod 3rd party):

def apple_print(f): 
    print "%sI am an Apple!" % f 

class Apple(Fruit): 
    print_callback = apple_print 

Jeśli uruchomić:

mac = Apple() 

chcę uzyskać:

Message from Fruit: I am an Apple!

Zamiast tego otrzymuję:

TypeError: apple_print() takes exactly 1 argument (2 given)

Myślę, że to dlatego, że self jest przekazywane jako pierwszy argument.

Jak napisać klasę Fruit? Dzięki!

+1

Doceniam problemy zostałeś mających z tym podejściem i doskonałe rozwiązania poniżej. Jednak wygląda na to, że powinieneś zdefiniować 'print_callback' jako metodę (może po prostu nazwać' print_me' i zdobyć podklasy, aby ją zastąpić.To byłoby proste i pythonic. Dla każdego innego rodzaju metody 'print_me', utwórz inną podklasę. –

+0

Moja motywacja do zadawania tego pytania polegała na tym, że chciałem podklasować google.appengine.ext.db.djangoforms (np. Jako DF2) w Google App Engine, aby umożliwić zastąpienie atrybutu formfield_callback w taki sam sposób jak modelForm Django (django.forms.ModelForm). Chodzi o to, że "klasa f (DF2)" może być następnie użyta przez standard Django po prostu przez zmianę superklasy na django.forms.ModelForm. Nie wiem, dlaczego autorzy Django zdefiniowali formfield_callback jako atrybut. –

Odpowiedz

7

Python zakłada, że ​​wszelkie funkcje związane z zakresem klasy są metodami. Jeśli chcesz, aby traktować je jako funkcje, trzeba grzebać w ich atrybutów do pobierania funkcji oryginalnego obiektu:

def __init__(self, *args, **kwargs): 
    super(Fruit, self).__init__(*args, **kwargs) 

    # The attribute name was changed in Python 3; pick whichever line matches 
    # your Python version. 
    callback = self.print_callback.im_func # Python 2 
    callback = self.print_callback.__func__ # Python 3 

    callback("Message from Fruit: ") 
+0

Albo jeszcze lepiej, po prostu dodaj do funkcji argument "self", aby działał jak normalna metoda. –

+0

Zgodnie z pytaniem, definicji wywołania zwrotnego nie można zmienić. –

+1

BTW, również warto zauważyć, że atrybut 'im_func' został zmieniony na' __func__' w Pythonie 3.x. –

2

Updated: zawierający abourget's suggestion używać staticmethod:

Spróbuj tego:

def __init__(self, *args, **kwargs): 
    super(Fruit, self).__init__(*args, **kwargs) 

    # Wrap function back into a proper static method 
    self.print_callback = staticmethod(self.print_callback) 

    # And now you can do: 
    self.print_callback("Message from Fruit: ") 
3

można użyć bezpośrednio:

class Apple(Fruit): 
    print_callback = staticmethod(apple_print) 

czyli

class Apple(Fruit): 
    print_callback = classmethod(apple_print) 

W pierwszym przypadku, dostaniesz tylko jeden parametr (oryginał). W drugim otrzymasz dwa parametry, z których pierwszy będzie klasą, na której został wywołany.

Mam nadzieję, że to pomaga, jest krótsze i mniej złożone.

+1

"Aby było jasne, ten kod nie może się zmienić, powiedzmy, że jest to kod innej firmy" –

0

Jest też nieco rozwiązanie dirtyer z metaclasses:

def apple_print(f): 
    print "Apple " + f 

class FruitMeta(type): 
    def __new__(cls, name, bases, dct): 
     func = dct["print_callback"] 
     dct["print_callback"]=lambda x,f,func=func: func(f) 
     return type.__new__(cls,name,bases,dct) 

class Fruit(object): 
    __metaclass__ = FruitMeta 
    print_callback = None 
    def __init__(self): 
     super(Fruit,self).__init__() 
     self.print_callback("Msg ") 

class Apple(Fruit): 
    print_callback = apple_print 

mac = Apple()here 

on manipuluje klasę przed jej tworzeniu!

1

Szukałem czegoś bardziej jak ten, kiedy znalazłem to pytanie:

class Something: 
    def my_callback(self, arg_a): 
     print arg_a 

class SomethingElse: 
    def __init__(self, callback): 
     self.callback = callback 

something = Something() 
something_else = SomethingElse(something.my_callback) 
something_else.callback("It works...") 
Powiązane problemy