If you store optional functionality of a base class in a secondary class, should the secondary class subclass the base class?

What I can get from your problem is that you want to have different functions and properties based on different condition, that sounds like good reason to use MetaClass. It all depends how complex your each class is, and what are you building, if it is for some library or API then MetaClass can do magic if used rightly.

MetaClass is perfect to add functions and property to the class based on some sort of condition, you just have to add all your subclass function into one meta class and add that MetaClass to your main class

From Where to start

you can read about MetaClass here, or you can watch it here. After you have better understanding about MetaClass see the source code of Django ModelForm from here and here, but before that take a brief look on how the Django Form works from outside this will give You an idea on how to implement it.

This is how I would implement it.

#You can also inherit it from other MetaClass but type has to be top of inheritance
class meta_class(type):
    # create class based on condition

    """
    msc: meta_class, behaves much like self (not exactly sure).
    name: name of the new class (ClassThatIsActuallyUsed).
    base: base of the new class (Base).
    attrs: attrs of the new class (Meta,...).
    """

    def __new__(mcs, name, bases, attrs):
        meta = attrs.get('Meta')
        if(meta.optionA){
            attrs['reset'] = resetA
        }if(meta.optionB){
            attrs['reset'] = resetB
        }if(meta.optionC){
            attrs['reset'] = resetC
        }
        if("QObject" in bases){
            attrs['do_stuff_that_only_works_if_my_children_also_inherited_from_QObject'] = functionA
        }
        return type(name, bases, attrs)


class Base(metaclass=meta_class): #you can also pass kwargs to metaclass here

    #define some common functions here
    class Meta:
        # Set default values here for the class
        optionA = False
        optionB = False
        optionC = False


class ClassThatIsActuallyUsed(Base):
    class Meta:
        optionA = True
        # optionB is False by default
        optionC = True

EDIT: Elaborated on how to implement MetaClass.


Let me start with another alternative. In the example below the Base.foo method is a plain identity function, but options can override that.

class Base:
    def foo(self, x):
        return x

class OptionDouble:
    def foo(self, x): 
        x *= 2  # preprocess example
        return super().foo(x)

class OptionHex:
    def foo(self, x): 
        result = super().foo(x)
        return hex(result)  # postprocess example

class Combined(OptionDouble, OptionHex, Base):
    pass

b = Base()
print(b.foo(10)) # 10

c = Combined()
print(c.foo(10)) # 2x10 = 20, as hex string: "0x14"

The key is that in the definition of the Combined's bases are Options specified before the Base:

class Combined(OptionDouble, OptionHex, Base):

Read the class names left-to right and in this simple case this is the order in which foo() implementations are ordered. It is called the method resolution order (MRO). It also defines what exactly super() means in particular classes and that is important, because Options are written as wrappers around the super() implementation

If you do it the other way around, it won't work:

class Combined(Base, OptionDouble, OptionHex):
    pass

c = Combined()
print(Combined.__mro__)
print(c.foo(10))  # 10, options not effective!

In this case the Base implementation is called first and it directly returns the result.

You could take care of the correct base order manually or you could write a function that checks it. It walks through the MRO list and once it sees the Base it will not allow an Option after it.

class Base:
    def __init_subclass__(cls, *args, **kwargs):
        super().__init_subclass__(*args, **kwargs)
        base_seen = False
        for mr in cls.__mro__:
            if base_seen:
                if issubclass(mr, Option):
                    raise TypeError( f"The order of {cls.__name__} base classes is incorrect")
            elif mr is Base:
                base_seen = True

    def foo(self, x): 
        return x

class Option:
    pass

class OptionDouble(Option):
    ... 

class OptionHex(Option):
    ... 

Now to answer your comment. I wrote that @wettler's approach could be simplified. I meant something like this:

class Base:
    def __init_subclass__(cls, *args, **kwargs):
        super().__init_subclass__(*args, **kwargs)
        print("options for the class", cls.__name__)
        print('A', cls.optionA)
        print('B', cls.optionB)
        print('C', cls.optionC)
        # ... modify the class according to the options ...

        bases = cls.__bases__
        # ... check if QObject is present in bases ...

    # defaults
    optionA = False
    optionB = False
    optionC = False


class ClassThatIsActuallyUsed(Base):
    optionA = True
    optionC = True

This demo will print:

options for the class ClassThatIsActuallyUsed
A True
B False
C True