I've got two class trees in my Python code:
BaseComponent BaseSeries
/ | \ |
Resistor Capacitor Inductor ESeries
The BaseSeries class tree implements preferred numbers such as the E-series, and generates sets of values between a pair of powers (e.g. [1.0, 2.2, 4.7, 10, 22, 47, 100, 220, 470] for the E3 series with exponents between 1 to 3).
By default, ESeries and any other instance of BaseSeries creates sequences of float objects. I'd like to use these classes to instead create sequences of Resistor, Capacitor and Inductor objects. Ideally, the individual Resistor, Capacitor, Inductor and ESeries classes would remain usable on their own (i.e. not rely on methods being implemented by other classes).
This sounds like a job for multiple inheritance, but I'm a bit confused about how best to implement this in Python (3). Ideally I'd like to just define something like:
class ResistorESeries(Resistor, ESeries):
pass
class CapacitorESeries(Capacitor, ESeries):
pass
class InductorESeries(Inductor, ESeries):
pass
in order to create classes that yield sequences of resistors, capacitors and inductors, but I don't know how best to tell BaseSeries instances to create objects of type Resistor, Capacitor and Inductor. I can think of two ways, but I can't decide which one is best, and I have a feeling there is a simpler, more Pythonic way that I'm missing:
have
BaseSeriescontain a property or variable pointing to the element type (e.g.Resistor) set either by the constructor, a class variable in the child class (e.g.Resistor.ELEMENT_TYPE = Resistor) or with an abstract property provided by the child class:class BaseSeries(object): ... def elements(self): # loop over numbers in this series for v in self.values(): yield self.element_type(v) @property @abc.abstractmethod def element_type(self): return NotImplemented class ESeries(BaseSeries): .... class BaseComponent(object): ... @property def element_type(self): return self class Resistor(BaseComponent): ... class ResistorESeries(Resistor, ESeries): # now BaseSeries' `element_type` property is provided by `BaseComponent` passThis would mean
ESeriescannot be used on its own as a concrete object, as it does not implement this property/variable, which is not ideal.use
selfwhen creating elements inBaseSeries, whereselfwill, as long asResistoris earlier in the method resolution order, refer to the desired element:class BaseSeries(object): ... def elements(self): # loop over numbers in this series for v in self.values(): # self here would refer to `Resistor` in # `ResistorESeries` instances yield self(v) class ESeries(BaseSeries): .... class BaseComponent(object): ... class Resistor(BaseComponent): ... class ResistorESeries(Resistor, ESeries): pass
This has the downside that, in instances of ESeries without being used as a mix-in, self will refer to itself, which does not support the correct __init__ signature.
So, does anyone have an idea of how best to do this in a Pythonic way, with maximum ability to reuse classes on their own?