I want to force implementation of these classes/function to keep the
same syntax for every new object, so that if I want to work with
another camera, I can keep my previous scripts, I just need to create
a new Camera object.
OT : do you really mean "object" (=> instance), or subclass ? But anyway:
Since you mentionned that "Sensor" and "Actuator" are not necessarily supposed to have the same interface for different BaseObject
subclasses, let's ignore the whole BaseObject
part for the moment and concentrate on the Camera
part.
If I understand correctly, what you want is generic Camera
type. This type must have a sensor
and an actuator
attributes, which both must respect a given interface, but with possibly different implementations.
At this point we (well, I at least) don't have enough context to decide if we need an abstract BaseCamera
type or if we just need abstract BaseCameraSensor
and BaseCameraActuator
interfaces. What is sure is that we do need BaseCameraSensor
and BaseCameraActuator
so let's start with this. I assume the sensor and actuator need to be aware of the camera they belong too, which FWIW really screams "strategy" pattern, so we start with a base class - let's call it "BaseStrategy" - that don't do much except get a reference to it's host object:
class Strategy(object):
def __init__(self, parent):
self._parent = parent
Now let's define our "CameraSensor" and "CameraActuator" interfaces:
class BaseCameraSensor(Strategy):
__metaclass__ = ABCMeta
@abstractmethod
def get_frame(self):
raise NotImplementedError
@abstractmethod
def set_parameter(self, value):
raise NotImplementedError
class BaseCameraActuator(Strategy):
__metaclass__ = ABCMeta
@abstractmethod
def random_method(self):
raise NotImplementedError
Now we can take care of the "Camera" part. If the only variant parts of the implementation are encapsulated in the Sensor
and Actuator
(which is the point of the strategy pattern), then we don't need an abstract base class - we can just pass the appropriate Sensor
and Actuator
subclasses as params to the initialiser:
class Camera(object):
def __init__(self, brand, model, sensor_class, actuator_class):
self.brand = brand
self.model = model
assert issubclass(sensor_class, BaseCameraSensor)
self.sensor = sensor_class(self)
assert issubclass(actuator_class, BaseCameraActuator)
self.actuator = actuator_class(self)
And the problem is solved. FWIW, note that you don't really need the Strategy
class nor the ABC part, just documenting the expected interface for sensor_class
and actuator_class
and let the program crash if they are not respected is enough - that's how we've been programming in Python for years (15+ years as far as I'm concerned) and it JustWork(tm) - but ABC at least makes the contract clearer and the program will crash sooner. But don't be fooled: it wont make your code foolsafe (hint: nothing will, period).
Now if we have some other variant parts that implementation cannot be known ahead of time, the simplest solution would be to follow the same pattern - delegate to a Strategy object that is passed at instanciation time. So the point is: now we have this implemented, we find out we don't have a need for some BaseCamera
ABC.