我正在编写一个用于模拟分布式传感器群的python平台.这个想法是最终用户可以编写一个由SensorNode行为(通信,日志记录等)组成的自定义节点,以及实现许多不同的传感器.
下面的例子简要说明了这个概念.
#prewritten class Sensor(object): def __init__(self): print "Hello from Sensor" #... #prewritten class PositionSensor(Sensor): def __init__(self): print "Hello from Position" Sensor.__init__(self) #... #prewritten class BearingSensor(Sensor): def __init__(self): print "Hello from Bearing" Sensor.__init__(self) #... #prewritten class SensorNode(object): def __init__(self): print "Hello from SensorNode" #... #USER WRITTEN class MySensorNode(SensorNode,BearingSensor,PositionSensor): def CustomMethod(self): LogData={'Position':position(), 'Bearing':bearing()} #position() from PositionSensor, bearing() from BearingSensor Log(LogData) #Log() from SensorNode
首先概述我想要实现的目标:我正在编写一个模拟器来模拟群体智能算法,特别关注移动传感器网络.这些网络由许多小型机器人组成,这些小型机器人传递单个传感器数据以构建复杂的环境感官图.
该项目的基本目标是开发一个模拟平台,为传感器提供抽象接口,使得相同的用户实现功能可以直接移植到运行嵌入式Linux的机器人群中.由于机器人实现是目标,我需要设计软件节点的行为相同,并且只能访问物理节点可能具有的信息.
作为模拟引擎的一部分,我将提供一组建模不同类型传感器和不同类型传感器节点的类.我希望从用户那里抽象出所有这些复杂性,以便所有用户必须做的是定义节点上存在哪些传感器,以及实现什么类型的传感器节点(移动,固定位置).
我最初的想法是每个传感器都会提供一个read()方法,它会返回相关的值,但是在阅读了问题的答案之后,我看到也许更具描述性的方法名称会有所帮助(.distance(),. position( ),. bearing()等).
我最初想要为传感器使用单独的类(具有共同的祖先),以便更技术的用户可以轻松地扩展其中一个现有类,以便在需要时创建新的传感器.例如:
Sensor | DistanceSensor(designed for 360 degree scan range) | | | IR Sensor Ultrasonic SickLaser (narrow) (wider) (very wide)
我最初考虑多重继承的原因(尽管它破坏了继承的IS-A关系)是由于模拟系统背后的基本原理.让我解释:
用户实现的MySensorNode不应该直接访问其在环境中的位置(类似于机器人,通过传感器接口间接访问),类似地,传感器不应该知道它们在哪里.然而,这种缺乏直接知识会带来问题,因为传感器的返回值都取决于它们在环境中的位置和方向(需要模拟它以返回正确的值).
SensorNode作为在模拟库中实现的类,负责在pygame环境中绘制MySensorNode - 因此,它是唯一应该直接访问环境中传感器节点的位置和方向的类.
SensorNode还负责环境中的平移和旋转,但是这种平移和旋转是电机驱动的副作用.
我的意思是机器人不能直接改变他们在世界上的位置,他们所能做的只是为电机提供动力,而世界范围内的运动是电机与环境相互作用的副作用.我需要在模拟中准确地对此进行建模.
因此,要移动,用户实现的功能可能会使用:
motors(50,50)
作为副作用,此调用将改变世界中节点的位置.
如果使用合成实现SensorNode,则SensorNode.motors(...)将无法直接更改实例变量(例如位置),也不能将MySensorNode.draw()解析为SensorNode.draw(),因此SensorNode imo应该使用继承实现.
就传感器而言,组合对这类问题的好处是显而易见的,MySensorNode由许多传感器组成 - 足够说.
然而,我认为问题在于传感器需要访问他们在世界中的位置和方向,如果你使用合成,你将最终得到如下调用:
>>> PosSensor.position((123,456)) (123,456)
然后再思考,你可以在初始化时将自己传递给传感器,例如:
PosSensor = PositionSensor(self)
然后
PosSensor.position()
然而,这个PosSensor.position()需要访问实例的本地信息(在init()期间作为self传递),那么为什么在本地访问信息时可以调用PosSensor呢?同样将你的实例传递给你所组成的对象似乎不太正确,跨越了封装和信息隐藏的界限(即使python对支持信息隐藏的想法没有太大帮助).
如果使用多重继承实现解决方案,这些问题将消失:
class MySensorNode(SensorNode,PositionSensor,BearingSensor): def Think(): while bearing()>0: # bearing() is provided by BearingSensor and in the simulator # will simply access local variables provided by SensorNode # to return the bearing. In robotic implementation, the # bearing() method will instead access C routines to read # the actual bearing from a compass sensor motors(100,-100) # spin on the spot, will as a side-effect alter the return # value of bearing() (Ox,Oy)=position() #provided by PositionSensor while True: (Cx,Cy)=position() if Cx>=Ox+100: break else: motors(100,100) #full speed ahead!will alter the return value of position()
希望这个编辑澄清了一些事情,如果你有任何问题,我很乐意尝试澄清它们
当构造MySensorNode类型的对象时,需要调用超类中的所有构造函数.我不想让用户必须为MySensorNode编写一个自定义构造函数,后者从每个超类中调用构造函数.理想情况下,我希望发生的是:
mSN = MySensorNode() # at this point, the __init__() method is searched for # and SensorNode.__init__() is called given the order # of inheritance in MySensorNode.__mro__ # Somehow, I would also like to call all the other constructors # that were not executed (ie BearingSensor and PositionSensor)
任何见解或一般评论将不胜感激,干杯:)
OLD EDIT:做类似的事情:
#prewritten class SensorNode(object): def __init__(self): print "Hello from SensorNode" for clss in type(self).__mro__: if clss!=SensorNode and clss!=type(self): clss.__init__(self)
这是有效的,因为self是MySensorNode的一个实例.但是这个解决方案很麻烦.
cdleary.. 11
如果您想坚持原始的数据地图设计,可以使用合成来解决传感器架构.你似乎是Python的新手,所以我会尽量把习语保持在最低限度.
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
通过使用mixins和代理(via __getattr__
)而不是继承来解决使用执行器描述的架构问题.(代理可以是继承的一个很好的替代方法,因为代理的对象可以在运行时绑定/解除绑定.此外,您不必担心使用此技术处理单个构造函数中的所有初始化.)
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!'
最后,您可以将所有内容与简单的节点声明一起抛出:
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] >= 0: self.composite_actuator.move(100, -100) my_node = MyNode() my_node.think()
这应该让您了解刚性类型系统的替代方案.请注意,您根本不必依赖类型层次结构 - 只需实现(可能是隐式的)公共接口.
少老:在仔细阅读了这个问题之后,我看到你拥有的是钻石继承的典型例子,这是让人们逃向单一继承的邪恶.
你可能不希望这开头,因为类层次结构意味着在Python中蹲下.你想要做的是做一个SensorInterface
(传感器的最低要求)并拥有一堆"mixin"类,它们具有完全独立的功能,可以通过各种名称的方法调用.在您的传感器框架中,您不应该说出类似的内容isinstance(sensor, PositionSensor)
- 您应该说"这个传感器可以进行地理定位吗?" 采用以下形式:
def get_position(sensor): try: return sensor.geolocate() except AttributeError: return None
这是鸭子类型哲学和EAFP(比要求更容易要求宽恕)的核心,这两者都是Python语言所包含的.
您应该描述这些传感器实际实现的方法,以便我们可以描述如何为插件架构使用mixin类.
旧:如果他们将代码写入一个放在插件包中的模块或者你有什么,你可以在导入他们的插件模块时神奇地为它们设置类.这段代码的一些内容(未经测试):
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
您可以在包中找到具有其他功能的模块.
如果您想坚持原始的数据地图设计,可以使用合成来解决传感器架构.你似乎是Python的新手,所以我会尽量把习语保持在最低限度.
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
通过使用mixins和代理(via __getattr__
)而不是继承来解决使用执行器描述的架构问题.(代理可以是继承的一个很好的替代方法,因为代理的对象可以在运行时绑定/解除绑定.此外,您不必担心使用此技术处理单个构造函数中的所有初始化.)
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!'
最后,您可以将所有内容与简单的节点声明一起抛出:
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] >= 0: self.composite_actuator.move(100, -100) my_node = MyNode() my_node.think()
这应该让您了解刚性类型系统的替代方案.请注意,您根本不必依赖类型层次结构 - 只需实现(可能是隐式的)公共接口.
少老:在仔细阅读了这个问题之后,我看到你拥有的是钻石继承的典型例子,这是让人们逃向单一继承的邪恶.
你可能不希望这开头,因为类层次结构意味着在Python中蹲下.你想要做的是做一个SensorInterface
(传感器的最低要求)并拥有一堆"mixin"类,它们具有完全独立的功能,可以通过各种名称的方法调用.在您的传感器框架中,您不应该说出类似的内容isinstance(sensor, PositionSensor)
- 您应该说"这个传感器可以进行地理定位吗?" 采用以下形式:
def get_position(sensor): try: return sensor.geolocate() except AttributeError: return None
这是鸭子类型哲学和EAFP(比要求更容易要求宽恕)的核心,这两者都是Python语言所包含的.
您应该描述这些传感器实际实现的方法,以便我们可以描述如何为插件架构使用mixin类.
旧:如果他们将代码写入一个放在插件包中的模块或者你有什么,你可以在导入他们的插件模块时神奇地为它们设置类.这段代码的一些内容(未经测试):
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
您可以在包中找到具有其他功能的模块.