2

I am looking for the way how to implement an object that has overwritable event handlers.

Here's a non-working code that I'd like to adjust so that it is working:

class Button(object):
  def __init__(self, id):
    self.id = id
    pass
  def trigger_on_press(self):
    self.on_press()
  def trigger_on_release(self):
    self.on_release()
  def on_press(self):
    # empty handler
    print("Just an empty on_press handler from id=%s" % self.id)
    pass
  def on_release(self):
    # empty handler
    print("Just an empty on_release handler from id=%s" % self.id)
    pass

btn = Button("btn")
btn.trigger_on_press()

def custom_handler(self):
  print("Event from id=%s" % self.id)

btn.on_press = custom_handler
btn.trigger_on_press()

How can I overwrite the default empty on_press method for that particular instance so that it is properly passed the self reference?

Passiday
  • 7,573
  • 8
  • 42
  • 61

2 Answers2

1

I would suggest an approach like this: you directly have an attribute (in this case func_on_press) that holds a reference to a function (not a method). That function receives a single argument which will be the object (I called it obj instead of self to make it clear that it is a function).

def default_empty_event_handler(obj):
    print('empty handler for id={}'.format(obj.the_id))

class Button:
    def __init__(self, the_id):
        self.the_id = the_id
        self.func_on_press = default_empty_event_handler
        self.func_on_release = default_empty_event_handler

    def trigger_on_press(self):
        self.func_on_press(self)    # we pass 'self' as argument to the function

    def trigger_on_release(self):
        self.func_on_release(self)  # we pass 'self' as argument to the function

Now you can change that attribute as you go:

btn = Button('btn')
print('first trigger')
btn.trigger_on_press()

def custom_handler(obj):
    print('custom handler for id={}'.format(obj.the_id))

btn.func_on_press = custom_handler
print('second trigger')
btn.trigger_on_press()

This will give the following output:

first trigger
empty handler for id=btn
second trigger
custom handler for id=btn

In my opinion this greatly reduces the code of the class (you define less methods) and is easily understandable. Does that work for you?

Ralf
  • 16,086
  • 4
  • 44
  • 68
  • Thanks, this works fine, however the duplicate question referenced by @Sanyash tifs me more. It feels more natural to call the event methods as normal methods, especially if I'd call them directly from the instance variable. – Passiday Apr 27 '19 at 14:12
0

the key is to use the class name in new assignement not the object

change btn.on_press = custom_handler to Button.on_press = custom_handler and magically it works

Explanation:

when you call btn.on_press() it will be translated to its original call of `Button.on_press(btn)', so you need to change the call in the blueprint not the object

your example:

class Button(object):
  def __init__(self, id):
    self.id = id
    pass
  def trigger_on_press(self):
    self.on_press()
  def trigger_on_release(self):
    self.on_release()
  def on_press(self):
    # empty handler
    print("Just an empty on_press handler from id=%s" % self.id)
    pass
  def on_release(self):
    # empty handler
    print("Just an empty on_release handler from id=%s" % self.id)
    pass

btn = Button("btn")
btn.trigger_on_press()

def custom_handler(self):
  print("Event from id=%s" % self.id)

Button.on_press = custom_handler  # here use the class name not obj name
btn.trigger_on_press()

output:

Just an empty on_press handler from id=btn
Event from id=btn
Mahmoud Elshahat
  • 1,873
  • 10
  • 24
  • This approach ovewrites the class method for all instances. Add `btx = Button("btx"); btx.trigger_on_press()` to the code and you'll see that it's already spoiled. – Passiday Apr 27 '19 at 13:59