1

I need to pass several methods as callbacks which don't take the self argument. This is how my current code looks like:

def _do_callback(callback, log, *args):
    try:
        # some common code
        callback(*args)
    except:
        log.error('XX')

class Foo(object):
    def __init__(self):
        self.log = Log('Foo')
        self.cb1_wrapper = lambda x: _do_callback(self.cb1, self.log, x)  # I need a function taking one parameter
        self.cb2_wrapper = lambda x: _do_callback(self.cb2, self.log, x)

    def cb1(self, x):
        # some code accessing self

    def cb2(self, x):
        # some code accessing self

    def register_callbacks(self):
        register('1', self.cb1_wrapper)
        register('2', self.cb2_wrapper)

Is it possible to write some decorator to apply to cb1 and cb2 to be able to pass the result into code that currently takes self.cb1_wrapper?

(I know the title is not ideal, feel free to edit)

wRAR
  • 25,009
  • 4
  • 84
  • 97
  • 1
    Do you mean you want to use a decorator to register methods as callbacks? You cannot use decorators for that because there is no instance when decorators are applied. – Martijn Pieters Jul 15 '14 at 14:30
  • @MartijnPieters well, actually I want to simplify the code. Using a decorator seems the right thing if you don't think about the implementation, so I thought maybe there is a solution. – wRAR Jul 15 '14 at 14:31
  • Sounds like your question is a dupe of [How to use decorator in observer pattern for Python 2.7](http://stackoverflow.com/q/24413619) then. – Martijn Pieters Jul 15 '14 at 14:33

2 Answers2

1

Sure; just think about how an unwrapped method should look:

def callback(fn):
    def inner(self, *args):
        return _do_callback(fn.__get__(self, type(self)), self.log, *args)
    return inner

class Foo(object):
    def __init__(self):
        self.log = Log('Foo')

    @callback
    def cb1_wrapped(self, x):
        pass
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • `@callback` returns a function that still takes self, doesn't it? – wRAR Jul 15 '14 at 14:39
  • @wRAR sure, that's what you want: see http://stackoverflow.com/questions/1367514/how-to-decorate-a-method-inside-a-class?rq=1 – ecatmur Jul 15 '14 at 14:40
  • @wRAR: this still requires you to register the method *on the instance*; you'd use `Foo().cb1_wrapped` and it returns a bound method that passes in `self` for you when called. That's the whole thing about callbacks, you need to register the *bound* method. – Martijn Pieters Jul 15 '14 at 14:42
  • Oh, I register callbacks inside a method of the same class (code sample updated) so it looks like the question is a lot easier? – wRAR Jul 15 '14 at 14:50
0

I don't know about a decorator, but you could easily write a "wrapper function" that turns out a bound method to a "callback". Something like that:

def wrap(bound_method):
    return lambda x: _do_callback(bound_method, bound_method.__self__.log, x)

The one (major?) drawback is that you have to use the wrapper at call time:

foo = Foo()
...
my_fnc_using_callback(wrap(foo.cb1))
Sylvain Leroux
  • 50,096
  • 7
  • 103
  • 125