Search code examples
pythonpython-3.xpygamepython-class

In python, what is the proper syntax for using a function as an attribute of a class?


I am new to programming, and I am trying to learn by making a simple game in pygame. I will be using a lot of clickable buttons, so I decided to make a class for them. As attributes, I want them to have a pygame.Rect object (to tell me where to put them on the screen), a pygame.Surface object (to tell me how it should look like) and a function that I call 'action' in the code below, which will be called whenever the button is clicked. Here is my attempt:

class Button:
    def __init__(self, rect, surf, action = None):
        self.rect = rect
        self.surf = surf
        self.action = action
        
    def draw(self, surf):
        surf.blit(self.surf, self.rect)

    def click(self, event):
        if event.type == pygame.MOUSEBUTTONUP:
            mouse_pos = pygame.mouse.get_pos()
            if self.rect.collidepoint(mouse_pos) and self.action != None:
                self.action()

For example, if I want to make a button that increases some global variable "money" by 1, then I can do

def change_money():
    global money
    money = money + 1

#some code here to define button_rect, button_surf

button = Button(button_rect, button_surf, change_money)

This does the job but it very unsatisfying, because if I wanted to make a new button that increases money by 2, I would have to define a new function change_money_2() and then do button2 = Button(button2_rect, button2_surf, change_money_2)

I tried the naive approach:

def change_money(delta):
    global money
    money = money + delta

button2 = Button(button_rect, button_surf, change_money(2))

but this does not work.

Question 1 What is the proper syntax for passing functions as attributes of classes; in particular, what do you do if the function has inputs?

Question 2 Ideally, I would like to not have to define a new function for the simple operation money = money + 1. Is there a way of passing such a function as an attribute of the class without having to give it a name? (for example, something like button = Button(button_rect, button_surf, [money -> money + 1]).

Edit 1: Thank you for the suggestion of using lambda functions; I did not know about these. Is there a way of having a lambda function take a global variable? The naive lambda global money : money = money + 2 or lambda : money = money + 2 do not seem to work. My intuition (based on Rabbid76's reference) is that these lambda functions are great for "mimicking" functions with a return statement, but what if I want to mimic a function that has no return statement, like change_money()?


Solution

  • Use a lambda:

    button2 = Button(button_rect, button_surf, change_money(2))

    button2 = Button(button_rect, button_surf, lambda: change_money(2))
    

    Is there a way of having a lambda function take a global variable?

    No, you can't use the global statement in the lambda expression. The lambda has no statement list, it is a single expression.