Skip to content
Advertisement

Python: Construct class (and variable names) through a function?

I recently started to work with Python’s classes, since I need to work with it through the use of OTree, a Python framework used for online experiment.

In one file, I define the pages that I want to be created, using classes. So essentially, in the OTree system, each class corresponds to a new page. The thing is, all pages (so classes) are basically the same, at the exception to some two parameters, as shown in the following code:

class Task1(Page):
    form_model = 'player'
    form_fields = ['Envie_WordsList_Toy']
    def is_displayed(self):
        return self.round_number == self.participant.vars['task_rounds'][1]
    def vars_for_template(player):
        WordsList_Toy= Constants.WordsList_Toy.copy()
        random.shuffle(WordsList_Toy)
        return dict(
            WordsList_Toy=WordsList_Toy
        )
    @staticmethod
    def live_method(player, data):
        player.WTP_WordsList_Toy = int(data)
    def before_next_page(self):
        self.participant.vars['Envie_WordsList_Toy'] = self.player.Envie_WordsList_Toy
        self.participant.vars['WTP_WordsList_Toy'] = self.player.WTP_WordsList_Toy

So here, the only thing that would change would be the name of the class, as well as the suffix of the variable WordsList_ used throughout this code, which is Toy.

Naively, what I tried to do is to define a function that would take those two parameters, such as this:

def page_creation(Task_Number,name_type):
    class Task+str(Task_Number)(Page):
        form_model = 'player'
        form_fields = ['Envie_WordsList_'+str(name_type)]

        def is_displayed(self):
            return self.round_number == self.participant.vars['task_rounds'][1]

        def vars_for_template(player):
            WordsList_+str(name_type) = Constants.WordsList+str(name_type).copy()
            random.shuffle(WordsList_+str(name_type))
            return dict(
                WordsList_+str(name_type)=WordsList_+str(name_type)
            )

        @staticmethod
        def live_method(player, data):
            player.WTP_WordsList_+str(name_type) = int(data)

        def before_next_page(self):
            self.participant.vars['Envie_WordsList_+str(name_type)'] = self.player.Envie_WordsList_+str(name_type)
            self.participant.vars['WTP_WordsList_+str(name_type)'] = self.player.WTP_WordsList_+str(name_type)

Obviously, it does not work since I have the feeling that it is not possible to construct variables (or classes identifier) this way. I just started to really work on Python some weeks ago, so some of its aspects might escape me still. Could you help me on this issue? Thank you.

Advertisement

Answer

You can generate dynamic classes using the type constructor:

MyClass = type("MyClass", (BaseClass1, BaseClass2), {"attr1": "value1", ...})

Thus, according to your case, that would be:

cls = type(f"Task{TaskNumber}", (Page, ), {"form_fields": [f"Envive_WordList_{name_type}"], ...})

Note that you still have to construct your common methods like __init__, is_displayed and so on, as inner functions of the class factory:

def class_factory(*args, **kwargs):
    ...
    def is_displayed(self):
        return self.round_number == self.participant.vars['task_rounds']

    def vars_for_template(player):
        ...

    # Classmethod wrapping is done below
    def live_method(player, data):
        ...
    cls = type(..., {
        "is_displayed": is_displayed, 
        "vars_for_template": vars_for_template,
        "live_method": classmethod(live_method),
        ...,
    }


@classmethod could be used as a function – {"live_method": classmethod(my_method)}

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement