Malik Brahimi
Malik Brahimi

Reputation: 16711

Anonymous Inner Class in Python

So I have a button class that does something when clicked, but different buttons perform different functions. I was wondering if there is such a thing as anonymous inner classes in Python to override such a callback without creating a new class altogether for each type of button. I know that I can assign a callback attribute to call such a function, but I am just wondering if there is an anonymous inner class equivalent.

class Button:
    def __init__(self):
        # set image here

    def update(self):
        if mouse_clicked():
            # do something

In Java I can create anonymous inner classes to override the update method for close, play, pause, and all other buttons without creating an entirely different class.

class Button {

    Button() {
        // set image here
    }

    public void update() {
        if mouse_clicked() {
            // do something
        }
    }
}

Upvotes: 1

Views: 1996

Answers (2)

Bakuriu
Bakuriu

Reputation: 102029

Sure, you can (although probably shouldn't) just use type:

In [1]: class Button:
   ...:     def callback(self):
   ...:         print('Hello')
   ...:         


In [2]: button = type('', (Button,), {'callback': lambda self: print('World')})()

In [3]: button.callback()
World

You may prefer to define the function outside the expression, so as to be able to avoid code-golfing it:

In [5]: def callback(self):
   ...:     print('World')
   ...: button = type('', (Button,), {'callback': callback})()
   ...: 

In [6]: button.callback()
World

This does exactly what Java is doing, however it does so more explicitly, and thus, with a more cumbersome syntax. In fact in python you can define local classes:

In [7]: def function():
   ...:     class MyButton(Button):
   ...:         def callback(self):
   ...:             print('Hello, from a local class!')
   ...:     return MyButton()
   ...: 

In [8]: button = function()

In [9]: button.callback()
Hello, from a local class!

The only difference with respect to Java is that you must give a name to the class and use it to create an instance. Using a decorator you could avoid this last step:

def auto_instantiator(*args, **kwargs):
    def decorator(cls):
        return cls(*args, **kwargs)
    return decorator

Used as:

In [2]: class Button:
   ...:     def callback(self):
   ...:         print('Button')
   ...:         

In [3]: @auto_instantiator()   # here args that should be passed to __init__
   ...: class button(Button):
   ...:     def callback(self):
   ...:         print('New Button')
   ...: # no need for button = button(...) 

In [4]: button.callback()    # button is an *instance* of the button class
New Button

However, depending on how you are going to use that method, I'd suggest two different ways to handle the problem:

  1. Actually all the actions of the buttons are the same, except for some data. In this case it's probably better to just create an instance attribute (or a property) to hold that data, and change the data instead of the method:

    I mean something like this:

    class Greeter:
        def __init__(self, name):
            self.name = name
        def greet(self):
            print('Hello, {.name}'.format(self))
    
  2. You can just set the instance attribute to the callback you want:

    button.callback = new_callback
    

Upvotes: 1

Marcus Müller
Marcus Müller

Reputation: 36462

You don't need to make an anonymous class -- python lets you replace methods with any callable (in fact, any object on the fly):

Button.update = lambda x: x*x 

is perfectly valid.

EDIT: The Java approach doesn't actually save you from generating any class -- that class just doesn't have a name, so there's no "benefit" doing that.

Upvotes: 0

Related Questions