Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The sensor architecture can be solved by using composition if you want to stick to your original map-of-data design. You seem to be new to Python so I'll try to keep idioms to a minimum.</p> <pre><code>class IRSensor: def read(self): return {'ir_amplitude': 12} class UltrasonicSensor: def read(self): return {'ultrasonic_amplitude': 63} class SickLaserSensor: def read(self): return {'laser_amplitude': 55} class CompositeSensor: """Wrap multiple component sensors, coalesce the results, and return the composite readout. """ component_sensors = [] def __init__(self, component_sensors=None): component_sensors = component_sensors or self.component_sensors self.sensors = [cls() for cls in component_sensors] def read(self): measurements = {} for sensor in self.sensors: measurements.update(sensor.read()) return measurements class MyCompositeSensor(CompositeSensor): component_sensors = [UltrasonicSensor, IRSensor] composite_sensor = MyCompositeSensor() measurement_map = composite_sensor.read() assert measurement_map['ultrasonic_amplitude'] == 63 assert measurement_map['ir_amplitude'] == 12 </code></pre> <p>The architectural problem you're describing with the actuators is solved by using mixins and <strong>proxying</strong> (via <code>__getattr__</code>) rather than inheritance. (Proxying can be a nice alternative to inheritance because objects to proxy to can be bound/unbound at runtime. Also, you don't have to worry about handling all initialization in a single constructor using this technique.)</p> <pre><code>class MovementActuator: def __init__(self, x=0, y=0): self.x, self.y = (x, y) def move(self, x, y): print 'Moving to', x, y self.x, self.y = (x, y) def get_position(self): return (self.x, self.y) class CommunicationActuator: def communicate(self): return 'Hey you out there!' class CompositeActuator: component_actuators = [] def __init__(self, component_actuators=None): component_actuators = component_actuators \ or self.component_actuators self.actuators = [cls() for cls in component_actuators] def __getattr__(self, attr_name): """Look for value in component sensors.""" for actuator in self.actuators: if hasattr(actuator, attr_name): return getattr(actuator, attr_name) raise AttributeError(attr_name) class MyCompositeActuator(CompositeActuator): component_actuators = [MovementActuator, CommunicationActuator] composite_actuator = MyCompositeActuator() assert composite_actuator.get_position() == (0, 0) assert composite_actuator.communicate() == 'Hey you out there!' </code></pre> <p>And finally, you can throw it all together with a simple node declaration:</p> <pre><code>from sensors import * from actuators import * class AbstractNode: sensors = [] # Set of classes. actuators = [] # Set of classes. def __init__(self): self.composite_sensor = CompositeSensor(self.sensors) self.composite_actuator = CompositeActuator(self.actuators) class MyNode(AbstractNode): sensors = [UltrasonicSensor, SickLaserSensor] actuators = [MovementActuator, CommunicationActuator] def think(self): measurement_map = self.composite_sensor.read() while self.composite_actuator.get_position()[1] &gt;= 0: self.composite_actuator.move(100, -100) my_node = MyNode() my_node.think() </code></pre> <p>That should give you an idea of the alternatives to the rigid type system. Note that you don't have to rely on the type hierarchy at all -- just implement to a (potentially implicit) common interface.</p> <h1>LESS OLD:</h1> <p>After reading the question more carefully, I see that what you have is a classic example of <a href="http://en.wikipedia.org/wiki/Diamond_problem" rel="nofollow noreferrer">diamond inheritance</a>, which is the <a href="http://code.activestate.com/recipes/146462/" rel="nofollow noreferrer">evil that makes people flee</a> towards single inheritance.</p> <p>You probably don't want this to begin with, since class hierarchy means squat in Python. What you want to do is make a <code>SensorInterface</code> (minimum requirements for a sensor) and have a bunch of "mixin" classes that have totally independent functionality that can be invoked through methods of various names. In your sensor framework you shouldn't say things like <code>isinstance(sensor, PositionSensor)</code> -- you should say things like "can this sensor geo-locate?" in the following form:</p> <pre><code>def get_position(sensor): try: return sensor.geolocate() except AttributeError: return None </code></pre> <p>This is the heart of duck-typing philosophy and <a href="http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Exceptions" rel="nofollow noreferrer">EAFP</a> (Easier to Ask for Forgiveness than Permission), both of which the Python language embraces.</p> <p>You should probably describe what methods these sensors will actually implement so we can describe how you can use mixin classes for your plugin architecture.</p> <h1>OLD:</h1> <p>If they write the code in a module that gets put in a plugin package or what have you, you can magically instrument the classes for them when you import their plugin modules. Something along the lines of this snippet (untested):</p> <pre><code> import inspect import types from sensors import Sensor def is_class(obj): return type(obj) in (types.ClassType, types.TypeType) def instrumented_init(self, *args, **kwargs): Sensor.__init__(self, *args, **kwargs) for module in plugin_modules: # Get this from somewhere... classes = inspect.getmembers(module, predicate=is_class) for name, cls in classes: if hasattr(cls, '__init__'): # User specified own init, may be deriving from something else. continue if cls.__bases__ != tuple([Sensor]): continue # Class doesn't singly inherit from sensor. cls.__init__ = instrumented_init </code></pre> <p>You can <a href="https://stackoverflow.com/questions/487971/is-there-a-standard-way-to-list-names-of-python-modules-in-a-package">find the modules within a package</a> with another function.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload