Writing a class that accepts a callback in Python?

I need to write a class that allows a subclass to set an attribute with the name of a function. Then this function should be called from class instances.

For example, I say that I need to write the Fruit class, where a subclass can convey a greeting message. The Fruit class must set the print_callback attribute, which can be set.

class Fruit(object): print_callback = None def __init__(self, *args, **kwargs): super(Fruit, self).__init__(*args, **kwargs) self.print_callback("Message from Fruit: ") 

I need to expose an API that can be used by this code (to be clear, this code cannot change, say, this is third-party code):

 def apple_print(f): print "%sI am an Apple!" % f class Apple(Fruit): print_callback = apple_print 

If I run:

 mac = Apple() 

I want to receive:

Posted by Fruit: I'm Apple!

Instead, I get:

TypeError: apple_print () takes exactly 1 argument (2 data)

I think this is because self is passed as the first argument.

So how do I write the Fruit class? Thanks!

+6
python callback
source share
5 answers

Python assumes that any functions associated with the scope of the class are methods. If you want to treat them like functions, you need to dig out their attributes to get the original function object:

 def __init__(self, *args, **kwargs): super(Fruit, self).__init__(*args, **kwargs) # The attribute name was changed in Python 3; pick whichever line matches # your Python version. callback = self.print_callback.im_func # Python 2 callback = self.print_callback.__func__ # Python 3 callback("Message from Fruit: ") 
+7
source share

You can use directly:

 class Apple(Fruit): print_callback = staticmethod(apple_print) 

or

 class Apple(Fruit): print_callback = classmethod(apple_print) 

In the first case, you will get only one parameter (original). In the second case, you will get two parameters, in which the first will be the class on which it was called.

Hope this helps and will be shorter and less complex.

+3
source share

Updated : Enabling abourget clause to use staticmethod :

Try the following:

 def __init__(self, *args, **kwargs): super(Fruit, self).__init__(*args, **kwargs) # Wrap function back into a proper static method self.print_callback = staticmethod(self.print_callback) # And now you can do: self.print_callback("Message from Fruit: ") 
+2
source share

I was looking for something similar when I found this question:

 class Something: def my_callback(self, arg_a): print arg_a class SomethingElse: def __init__(self, callback): self.callback = callback something = Something() something_else = SomethingElse(something.my_callback) something_else.callback("It works...") 
+1
source share

There's also a slightly messier solution with metaclasses:

 def apple_print(f): print "Apple " + f class FruitMeta(type): def __new__(cls, name, bases, dct): func = dct["print_callback"] dct["print_callback"]=lambda x,f,func=func: func(f) return type.__new__(cls,name,bases,dct) class Fruit(object): __metaclass__ = FruitMeta print_callback = None def __init__(self): super(Fruit,self).__init__() self.print_callback("Msg ") class Apple(Fruit): print_callback = apple_print mac = Apple()here 

He manipulates the class before it was created!

0
source share

All Articles