Putting Liskov substitution principal to one side, I had a suggestion to follow PEP3102, and do `def train(self, *, epochs):`…
It's a rather simple suggestion that I just might take aboard. In response to Dieter: My purpose for using a base class is so that the highest level interface—say a CLI, GUI, REST and RPC API—can all be configured in one place, but enable the greatest possible divergence from a parameter standpoint. An alternative here [in this domain] would be to create a new Keras/scikit.learn. I.e., one consistent interface that is to be implemented in full by each framework. The reason I don't want to take this alternative is manyfold, that I don't need to get in here (happy to start a new thread if you're interested). Two major advantages of this approach are: 0. Implementers can be [pretty much] as divergent as they want, with different default and required parameters; and even semantics [though I do have `assert`s to force `method0` to be executed before `method1`]; 1. Implementers don't need to create their own CLIs, GUIs, REST and RPC APIs Two major disadvantages: 0. Parameters aren't known ahead of time, so you can do the whole `Animal` [duck type] trick where `Animal` could actually be `Dog` or `Horse` [making the obvious Base `ABC` & `abstractmethod` approach 'wrong'] 1. Each implementer framework can maintain wildly different internal APIs, making more hardcore integrations—in a multi-ML sense—far more difficult Samuel Marks Charity <https://sydneyscientific.org> | consultancy <https://offscale.io> | open-source <https://github.com/offscale> | LinkedIn <https://linkedin.com/in/samuelmarks> On Sat, Aug 29, 2020 at 8:24 PM Dieter Maurer <die...@handshake.de> wrote: > > Samuel Marks wrote at 2020-8-29 19:14 +1000: > >So there is no way to get a AOT error when the two functions aren't > >implemented, if the two functions have different required arguments on > >implementors? > > > >To paint this picture for why I need this, say the first is: > > > >class Base(ABC): > > @abstractclass > > def train(self, epochs): > > asset epochs is not None and epochs > 0 > > > >…and the implementation is: > > > >class TensorFlow(Base): > > def train(self, learning_rate, epochs, callbacks, metrics, loss, > > optimizer): > > super(Base, self).__init__(epochs=epochs) > > > >[+ docstrings and PEP484 types; excluded here for concision] > > > >So how do I enable this use-case? - Obviously it doesn't make sense to > >include these on the base class, and giving them default values > >probably doesn't make sense either. > > > >You're saying I shouldn't be using ABC for this. So what should I be using? > > What is your purpose to use a base class in the first place -- > and especially one where `train` has this signature? > > You signal with this base class, that it makes sense to > call `train` with a single positional argument. > But `train` in the derived class cannot be called in this way. > > Base classes are there to capture common features of several > related classes. In your example above, this is not the case. > Do not use base classes in this way. > > >The requirement I'm trying to enforce is that each implementor has a > >`train` callable attached. > >Preferably with one required field (but this is just a nice-to-have). > > Read the Python documentation about metaclasses and the special methods > related to class derivation. Both approaches would allow you > to check whatever you want. > > > ... > >However if I only have functions which accept an instance of a class > >as the argument, then that will make it less user-friendly to the API > >caller. So I really am looking for handling both interfaces in a > >straightforward manner. > > Any system makes some things easy and other things difficult. > Try to stay with the essential paradigms underlaying the system -- > in order to use the easy things whenever possible. > > In your case, this means that the signature of an overriding > method must be quite similar to that of the overridden method. -- https://mail.python.org/mailman/listinfo/python-list