S
S
Sergey Ermakov2016-04-07 10:16:06
Python
Sergey Ermakov, 2016-04-07 10:16:06

How to make the execution of a function as an Event?

Hello, tell me please.

# file1.py

class TEMP1(object):
    def __init__(self): pass
    def func1(self, *a, **k):
        print 'TEMP1 > func1'
        
t1 = TEMP1()
        
# file2.py

#from file1 import TEMP1

def decorate(function):
    print 'start decorate'
    def wrap(*a, **k):
        print 'start wrap'
        return function(*a, **k)
    return wrap

@decorate
def new_func1(*a, **k):
    old_func1(*a, **k)
    print 'hook: new_func1'
    
old_func1 = TEMP1.func1
TEMP1.func1 = new_func1

def myFunc():
    print 'myFunc'

t1.func1()

How to define our function as an Event in the decorate function (if I understand correctly), so that we can write function += myFunc to it ?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
Vindicar, 2016-04-07
@Vindicar

Wait, wait, wait... if I understand correctly, you want to create a class with the following properties:
1. Class instances must store a list of custom callback functions with the same signature.
2. When calling an instance of this class (as a callable object), all callback functions must be called in turn.
3. It should be possible to add and remove a new callback using the += and -= operators.
Did I get it right? Then it is not clear to me what the decorator has to do with it, and what the function being decorated will have to do.
I would write something along the lines of:

class EventCaller:
    #конструктор
    def __init__(self):
        #поскольку мы не планируем позволять двойную подписку, то у нас будет множество, а не список.
        self.__callbacks = set();
    # магический метод перегрузки оператора +=
    def __iadd__(self, callback):
        #проверка, что нам вообще передали callable object
        if not callable(callback):
            raise Exception("Callback object is not callable")
        #эта проверка не позволяет подписаться дважды одним объектом
        if callback in self.__callbacks:
            raise Exception("Double event subscription")
        self.__callbacks.add(callback)
    #магический метод перегрузки оператора -=
    def __isub__(self, callback):
        if callback not in self.__callbacks:
            raise Exception("No such event handler")
        self.__callbacks.remove(callback)
    #магический метод, позволяющий вызывать экземпляр объекта как функцию
    #аргументы, переданные методу, сохраняются как есть
    #неименованные - в кортеже args, именованные - в словаре kwargs
    def __call__(self, *args, **kwargs):
        for callback in self.__callbacks:
            try:
                callback(*args, **kwargs)
            except:
                pass

Here is a draft of such a class. Of course, it needs to be improved:
a) it is worth adding thread-safe work with the list of handlers. Perhaps replace the list with thread-safe storage.
b) it is worth remembering the exceptions thrown by handlers, and reporting them in one way or another (rethrow? throw your own exception with a list of all that happened?)
c) so that if one of the handlers changes the passed objects, this will affect the subsequent ones. If this behavior is to be avoided, it may be worth creating a separate deep copy (see the copy module) of args and kwargs for each handler. Although there are some troubles.
Usage example:
#простой обработчик, печатающий переданные ему аргументы
def evthandler(*args, **kwargs):
    print "Unnamed arguments are:", args
    print "Named arguments are:", kwargs
#
#создаем объект-событие
evt = EventCaller()
#подписываемся на него
evt += evthandler
#генерируем событие. Аргументы могут быть произвольными, они будут переданы обработчикам как есть
evt("unnamed arg 1", "unnamed arg 2", kwarg1=True, kwarg2=42)
#отписываемся
evt -= evthandler
#этот вызов не будет иметь эффекта, так как список обработчиков события пуст
evt("unnamed arg 1", "unnamed arg 2", kwarg1=True, kwarg2=42)

I can't vouch for the syntactical correctness of the code, but the idea should be clear.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question