Assigning Values to Subsets of Subsets

I have four lists of a custom Trial object, which contains data about individual trials in an experiment. The lists are MC_inducer_con, MC_inducer_diamond_con, MC_inducer_orientation_con, and MC_inducer_color_con from here on referred to as con, diamond, orientation, and color respectively. color is a subset of orientation is a subset of diamond is a subset of con. Depending on whether or not a given Trial object is in each of these lists determines what values are assigned to it. My problem is twofold, depending on the method I use to implement these conditions on each trial.

If I evaluate each subset with a set of nested if statements:

if self.trial in self.MC_inducer_diamond_con:
    self.trial.attr["target_shape"] = TARGET_SHAPES[rand_shape]

    if self.trial in self.MC_inducer_orientation_con:
        self.trial.attr["target_orientation"] = ORIENTATIONS[rand_orientation]
                            
        if self.trial in self.MC_inducer_color_con:
            self.trial.attr["target_color"] = COLORS[rand_color]
        else:
            self.trial.attr["target_color"] = COLORS[1 if rand_color == 0 else 0]
    else:
        self.trial.attr["target_orientation"] = ORIENTATIONS[1 if rand_orientation == 0 else 0]
else:
    self.trial.attr["target_shape"] = TARGET_SHAPES[1 if rand_shape == 0 else 0]

I run into a problem where if a trial is in a superset but not a subset (for example if in diamond but not orientation, color or con) then the rest of the code is not evaluated and the remaining fields in the Trial objects are left empty. What the code should do is assign each trial a different value if not in a given set.

If instead I structure the code like this:

if self.trial in self.MC_inducer_diamond_con:
    self.trial.attr["target_shape"] = TARGET_SHAPES[rand_shape]
elif self.trial in self.MC_inducer_diamond_con and self.trial in self.MC_inducer_orientation_con:
    self.trial.attr["target_shape"] = TARGET_SHAPES[rand_shape]
    self.trial.attr["target_orientation"] = ORIENTATIONS[rand_orientation]
elif self.trial in self.MC_inducer_diamond_con and self.trial in self.MC_inducer_orientation_con and self.trial in self.MC_inducer_color_con:
    self.trial.attr["target_shape"] = TARGET_SHAPES[rand_shape]
    self.trial.attr["target_orientation"] = ORIENTATIONS[rand_orientation]
    self.trial.attr["target_color"] = COLORS[rand_color]

I run into a separate issue where I can only have one else statement to encapsulate a multitude of other possibilities (essentially, each combination of being/not being in each set).

Can anyone point me in the right direction for correctly evaluating these possibilities? Maybe taking advantage of class inheritances? I can be more specific about the requirements if need be, but I'm hoping a solution can be found with even this information.


Why don't you use a separate if-else for each? As far as I understand from your examples, one does not affect the other one. So, something like this should work:

if self.trial in self.MC_inducer_diamond_con:
    self.trial.attr["target_shape"] = TARGET_SHAPES[rand_shape]
else:
    self.trial.attr["target_shape"] = TARGET_SHAPES[1 if rand_shape == 0 else 0]

if self.trial in self.MC_inducer_orientation_con:
    self.trial.attr["target_orientation"] = ORIENTATIONS[rand_orientation]
else:
    self.trial.attr["target_orientation"] = ORIENTATIONS[1 if rand_orientation == 0 else 0]
                            
if self.trial in self.MC_inducer_color_con:
    self.trial.attr["target_color"] = COLORS[rand_color]
else:
    self.trial.attr["target_color"] = COLORS[1 if rand_color == 0 else 0]

If this does not help, please share the data structure by abstracting the details of your case. For example, no need to use self in the examples, sharing the structure of the Trial object would be helpful, namings can be more simple, etc. So, we can see your need more easily.