Mik*_*mer 14 python oop constructor multiple-inheritance
我正在编写一个用于模拟分布式传感器群的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
Run Code Online (Sandbox Code Playgroud)
首先概述我想要实现的目标:我正在编写一个模拟器来模拟群体智能算法,特别关注移动传感器网络.这些网络由许多小型机器人组成,这些小型机器人传递单个传感器数据以构建复杂的环境感官图.
该项目的基本目标是开发一个模拟平台,为传感器提供抽象接口,使得相同的用户实现功能可以直接移植到运行嵌入式Linux的机器人群中.由于机器人实现是目标,我需要设计软件节点的行为相同,并且只能访问物理节点可能具有的信息.
作为模拟引擎的一部分,我将提供一组建模不同类型传感器和不同类型传感器节点的类.我希望从用户那里抽象出所有这些复杂性,以便所有用户必须做的是定义节点上存在哪些传感器,以及实现什么类型的传感器节点(移动,固定位置).
我最初的想法是每个传感器都会提供一个read()方法,它会返回相关的值,但是在阅读了问题的答案之后,我看到也许更具描述性的方法名称会有所帮助(.distance(),. position( ),. bearing()等).
我最初想要为传感器使用单独的类(具有共同的祖先),以便更技术的用户可以轻松地扩展其中一个现有类,以便在需要时创建新的传感器.例如:
Sensor
|
DistanceSensor(designed for 360 degree scan range)
| | |
IR Sensor Ultrasonic SickLaser
(narrow) (wider) (very wide)
Run Code Online (Sandbox Code Playgroud)
我最初考虑多重继承的原因(尽管它破坏了继承的IS-A关系)是由于模拟系统背后的基本原理.让我解释:
用户实现的MySensorNode不应该直接访问其在环境中的位置(类似于机器人,通过传感器接口间接访问),类似地,传感器不应该知道它们在哪里.然而,这种缺乏直接知识会带来问题,因为传感器的返回值都取决于它们在环境中的位置和方向(需要模拟它以返回正确的值).
SensorNode作为在模拟库中实现的类,负责在pygame环境中绘制MySensorNode - 因此,它是唯一应该直接访问环境中传感器节点的位置和方向的类.
SensorNode还负责环境中的平移和旋转,但是这种平移和旋转是电机驱动的副作用.
我的意思是机器人不能直接改变他们在世界上的位置,他们所能做的只是为电机提供动力,而世界范围内的运动是电机与环境相互作用的副作用.我需要在模拟中准确地对此进行建模.
因此,要移动,用户实现的功能可能会使用:
motors(50,50)
Run Code Online (Sandbox Code Playgroud)
作为副作用,此调用将改变世界中节点的位置.
如果使用合成实现SensorNode,则SensorNode.motors(...)将无法直接更改实例变量(例如位置),也不能将MySensorNode.draw()解析为SensorNode.draw(),因此SensorNode imo应该使用继承实现.
就传感器而言,组合对这类问题的好处是显而易见的,MySensorNode由许多传感器组成 - 足够说.
然而,我认为问题在于传感器需要访问他们在世界中的位置和方向,如果你使用合成,你将最终得到如下调用:
>>> PosSensor.position((123,456))
(123,456)
Run Code Online (Sandbox Code Playgroud)
然后再思考,你可以在初始化时将自己传递给传感器,例如:
PosSensor = PositionSensor(self)
Run Code Online (Sandbox Code Playgroud)
然后
PosSensor.position()
Run Code Online (Sandbox Code Playgroud)
然而,这个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()
Run Code Online (Sandbox Code Playgroud)
希望这个编辑澄清了一些事情,如果你有任何问题,我很乐意尝试澄清它们
当构造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)
Run Code Online (Sandbox Code Playgroud)
任何见解或一般评论将不胜感激,干杯:)
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)
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为self是MySensorNode的一个实例.但是这个解决方案很麻烦.
cdl*_*ary 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
Run Code Online (Sandbox Code Playgroud)
通过使用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!'
Run Code Online (Sandbox Code Playgroud)
最后,您可以将所有内容与简单的节点声明一起抛出:
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()
Run Code Online (Sandbox Code Playgroud)
这应该让您了解刚性类型系统的替代方案.请注意,您根本不必依赖类型层次结构 - 只需实现(可能是隐式的)公共接口.
在仔细阅读了这个问题之后,我看到你拥有的是钻石继承的典型例子,这是让人们逃向单一继承的邪恶.
你可能不希望这开头,因为类层次结构意味着在Python中蹲下.你想要做的是做一个SensorInterface(传感器的最低要求)并拥有一堆"mixin"类,它们具有完全独立的功能,可以通过各种名称的方法调用.在您的传感器框架中,您不应该说出类似的内容isinstance(sensor, PositionSensor)- 您应该说"这个传感器可以进行地理定位吗?" 采用以下形式:
def get_position(sensor):
try:
return sensor.geolocate()
except AttributeError:
return None
Run Code Online (Sandbox Code Playgroud)
这是鸭子类型哲学和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
Run Code Online (Sandbox Code Playgroud)