Search code examples
pythonobjectdecorator

Pass instance attribute as a decorator parameter


I am creating a class Encuesta to create multiple surveys via user commands for a discord bot using discord.py. To achieve this, I have to pass the attribute "self.buttonid" as the 'custom_id' parameter for the decorator @discord.ui.button (already defined by discord.py).

class Encuesta(discord.ui.View):

    def __init__(self, pregunta, buttonid):
        super().__init__(timeout=None)
        self.pregunta = pregunta
        self.buttonid = buttonid

    @discord.ui.button(label="Realizar encuesta", style=discord.ButtonStyle.grey, custom_id=self.buttonid)
    async def encuesta(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.send_modal(await crear_modal(self.pregunta, interaction.user))

But I get the error "self is not defined", since 'self' only exists inside class methods, and cant access it from the decorator calling.

I just need to get access to self.buttonid while calling the decorator.

I tried directly calling self.buttonid, without success. Other answers rely on modifyng the decorator, and I would want to avoid that.


Solution

  • Since a discord View class is meant to be instantiated only once, one viable workaround would be to use a class factory function to create a singleton class with buttonid preserved in the closure of the factory function rather than storing it as an instance attribute:

    def Encuesta(pregunta, buttonid):
        class _Encuesta(discord.ui.View):
            def __init__(self):
                super().__init__(timeout=None)
        
            @discord.ui.button(
                label="Realizar encuesta",
                style=discord.ButtonStyle.grey,
                custom_id=buttonid)
            async def encuesta(self, interaction: discord.Interaction, button: discord.ui.Button):
                await interaction.response.send_modal(await crear_modal(pregunta, interaction.user))
    
        return _Encuesta()