Dekorowanie metod z klasy

0

Dzień dobry. Piszę sobie irc-bota z systemem pluginów. Celem jest jak największe uproszczenie dodawania owych pluginów. Jak na razie udało mi się osiągnąć to, że wystarczy do katalogu /plugins dorzucić plik z klasą dziedziczącą po plugin. Przy starcie programu wczytywane są wszystkie takie pluginy i kiedy na ircu przyjdzie jakaś wiadomość, na każdym z nich jest wywoływana metoda on_pubmsg. Tutaj kod: https://github.com/pingwindyktator/pybot

Chciałbym jednak bardziej ułatwić wywoływanie prostych komend, tj kiedy ktoś napisze na ircu:
> nick_bota: komenda argumenty do komendy
to zostanie wywołana funkcja komenda(argumenty do komendy) z pluginu który taką funkcję rejestruje. Chcę to zrealizować za pomocą dekoratorów, tj:

@memoize('komenda')
def some_func()

Problem jest jednak taki, że owe funkcje będą metodami w klasie. Finalnie ma to wyglądać zatem tak: http://melpon.org/wandbox/permlink/Spx6TxwDNojeNOoA
To co udało mi się zrobić: http://melpon.org/wandbox/permlink/XISE5u938NdiOMhE
jednak tutaj muszę najpierw wywołać funkcję (linia 40) żeby wowołał się dekorator i zapisał sobie instancję klasy tej funkcji (self, linia 9)

Jakieś pomysł?

1

Z tego co rozumiem chcesz modyfikować obiekt bota pod wpływem komend?
Jeśli tak, to możesz mieć problemy z zależnościami pluginów.

Na Twoim miejscu zrobił bym pośrednika proxy, do którego podłączał bym bota, a gdy nastąpi zmiana tworzył bym builderem obiekt od nowa. Modyfikowanie obiektu "w locie" przy wzroście funkcjonalności to chyba średni pomysł, nieprawdaż?

Komendy dodatkowo zrobił bym na interpreterze, (nie patrzyłem na kod źródłowy na gicie).

0

@Loop end nie do końca rozumiem, ale chyba
Z tego co rozumiem chcesz modyfikować obiekt bota pod wpływem komend?
Nie. Chcę rejestrować komendy, funkcje, które bot będzie poźniej wywoływał. Czyli najpierw zebrać wszystkie funkcję które rejestrują się (@command('some command')), później móc wywoływać takie funkcje mając wyłącznie some command.

1

Czyli korzystasz ze zwykłej kompozycji. Czyli gdzie Ci jest potrzebny dekorator? Co chcesz tak właściwie zmieniać? Nie bardzo rozumiem.

Jak na mój gust powinieneś stworzyć interpretator, który to da "moc" botowi. Na przykład (wywal wszystkich użytkowników według wzorca "[aA]*").
Następnie podzielił bym to na sekcje zadaniowe (tego interpretatora), do wypisywania, modowania itd.
Na podstawie języka interpretatora tworzyłbym pluginy.
Ilość pracy włożonej w stworzenie interpretera, szybko się zwróci jeżeli będzie dużo pluginów, a widzę że z takim zamysłem tworzony jest soft.

0

Dokładnie tak, chcę możliwie uprościć pisanie pluginów - kod bota, interpretera może być dowolnie długi i zagmatwany. Opiszę może problem szerzej.

  1. Metody rejestrują się jako komendy bota
  2. Bot wywołuje te komendy mając jedynie nazwę metody

I tutaj próba zaimplementowania tego: http://melpon.org/wandbox/permlink/Qzp3s0dQVf5Vz5wv
Pojawią się zatem problem opisywany w pierwszym poście - brak informacji o instancji klasy.

0

@Loop end pochwalę się:
http://melpon.org/wandbox/permlink/j6jtSxSqPZoluEMM

Rozwiązanie okazało się proste. Dekorator dodaje jakiś artybut do metody, później bot sprawdza obecność tego artybutu w każdej funkcji z zarejestrowanych pluginów. Jeśli taki atrybut jest - bingo.

0

lepiej bylo zrobic w kazdym pluginie metode init i w niej wywolywać register_command czy cos i ta metoda dorzucała by komendę i metode którą ma ona wywoływać do jakiejś globalnej listy

1

Wcześniejszą wersję mógłbyś zaimplementować w ten sposób:

from functools import partial

_command_to_func_map = {}


def memoize(cmd):
    def deco(func):
        _command_to_func_map[cmd] = func
        def deco_impl(self):
            func(self)

        return deco_impl

    return deco


def call_func(cmd):
    func = _command_to_func_map[cmd]
    func()


class base(object):

    def __new__(cls, *args, **kwargs):
        self = super(base, cls).__new__(cls, *args, **kwargs)
        for key_func in _command_to_func_map:
            _command_to_func_map[key_func] = partial(_command_to_func_map[key_func], self)
        return self

    def __init__(self):
        pass


class bar(base):

    @memoize('some command')
    def foo(self):
        print('bar.foo')


if __name__ == '__main__':
    b = bar()
    call_func('some command')

1 użytkowników online, w tym zalogowanych: 0, gości: 1