0

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.

1 Answers1

1

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)}

Icebreaker454
  • 1,031
  • 7
  • 12
  • Thank you for your answer. However, I still struggle on the process to define functions inside the scope of the class. For instance, for `isdisplayed`, where do I define the `return self.round_number == self.participant.vars['task_rounds'][1]`? – Artengo Polienko Mar 25 '21 at 17:47