padfoot
padfoot

Reputation: 20

Can I store an instance method as an attribute in Python?

I have a Python class that allows a user to register a callback. I am trying to provide a default callback but I'm not sure how to do it.

First attempt:

class MyClass:
    callback = printing_callback

    def register_callback(self, callback):
        self.callback = callback

    def printing_callback(self, message):
        print(f"Message: {message}")

    def notify(self, message):
        self.callback(message)

This gave me an 'unresolved reference' error for printing_callback

Second attempt:

I tried changed the line to callback = self.printing_callback. This gave me an 'unresolved reference' error for self

Third attempt:

callback = lambda message: print(f"Message: {message}")

which gave me this warning: "PEP 8: E731 do not assign a lambda expression, use a def"

Is there a way to initialize callback to a default?

Update

I found a way to set the default method and that is not to have printing_callback be an instance method - which makes sense. This appears to compile without warnings

def printing_callback(message):
    print(f"Message: {message}")


class MyClass:
    callback = printing_callback

    def register_callback(self, callback):
        self.callback = callback

    def notify(self, message):
        self.callback(message)

But now the when printing_callable is called it is called with an extra argument - the MyClass instance that called it.

I can change the signature to printing_callback(myClass, message) and the code works. Are there cleaner ways to do this than to just have an extra parameter?

Upvotes: 0

Views: 109

Answers (2)

wjandrea
wjandrea

Reputation: 33107

Set the default on initialization.

def printing_callback(message):
    print(f"Message: {message}")


class MyClass:
    def __init__(self, callback=printing_callback):
        self.callback = callback

    def notify(self, message):
        self.callback(message)

As far as I can tell, there's no reason for callback to be a class attribute, so make it an instance attribute, and that avoids it being registered as a method.

If you need to change it later, you can simply change the callback attribute instead of using the setter register_callback():

m = MyClass()
m.notify('Hello!')  # -> Message: Hello!

m.callback = print
m.notify('Hi!')  # -> Hi!

Upvotes: 2

S.B
S.B

Reputation: 16526

In first attempt, it's obvious that you have no reference to printing_callback, you didn't define it.

In second: self parameter is which get filled by python, when you call that method on an instance of the class. It points to the newly created object which is the class instance ofcourse. note that it is local to the methods, not inside the body of your class. so no reference again.

The structure you are looking for is :

def printing_callback(self, message):
    print(f"Message: {message}")


class MyClass:
    callback = printing_callback

    def register_callback(self, callback):
        self.callback = callback

    def notify(self, message):
        self.callback(message)
        
obj = MyClass()
obj.callback("testing")

Note I added an extra parameter self (the actual name doesn't matter).This is because we call that function on an instance, so again python fill the first argument with the reference to that instance. This is why I named it self.

Upvotes: 0

Related Questions