Skip to content

Improve subclassing of models #4035

Description

@embray

Subclassing a model with parameters is currently no easy task. Subclassing an abstract model with no parameters or evaluate defined is easy. For example we provide Fittable1DModel and Fittable2DModel as base classes for many models, but these do little more than provide default inputs and outputs as well as other class attributes like fittable = True as a convenience.

But subclassing a model with parameters is currently quite fraught with difficulties. In particular, if you subclass a model but have different parameters in the subclass than those in the base class. I hoped that #3679 would improve matters here, and it does, but not enough. With #3679, when you subclass a model by default you don't inherit any of that model's parameters. If you want your subclass to have some of the same parameters you include them manually in the class definition. However, it also makes it possible to inherit parameters by including _inherit_descriptors_ = (Parameter,).

Overriding or inheriting parameters partially works. The resulting subclass will have the correct set of Parameter descriptors. However, much of the difficulty comes in the __init__. The default __init__ method that gets generated in this case is terribly broken, because by default it calls super(cls).__init__(...) and passes down all arguments to the super class's __init__, which may not take all the same parameters. So I really need to come up with a better default __init__ implementation that allows only the relevant arguments to be picked off by base class __init__s, but still assembles all the arguments properly when it gets down to Model.__init__.

This can also be worked around by hand-writing an __init__ in the subclass. It can work if it passes in all arguments as keyword arguments, but that's tedious and ugly. A more careful default __init__ implementation should enable this to be handled better.

What's even trickier though is you subclass a model and don't inherit any of its Parameters. The effect is that you can still access the subclass's Parameter descriptors due to the normal attribute access rules of Python. But because the parameter initialization code is working only on the subclass it ignores the base class's parameters and doesn't set values for them. One thought might be to override OrderedDescriptorContainer.__getattribute__ so that it doesn't allow Parameter lookups from base classes when parameters aren't inherited. Another option would be to just always require Parameters to be inherited when subclassing, which would simplify matters.

__call__ has some of the same problems as __init__ for Models, especially if the subclass changes the number of inputs or outputs. This is something that we might choose to disallow altogether, though one might be able to come up with a use case for it (when going reducing the number of inputs, anyways, there needs to be a way to provide defaults for any inputs that are factored out).

Activity

  1. added this to the Future milestone on Aug 4, 2015
  2. removed this from the Future milestone on Apr 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions