当前位置:  开发笔记 > 编程语言 > 正文

讨论项目的多重继承与组合(+其他事项)

如何解决《讨论项目的多重继承与组合(+其他事项)》经验,为你挑选了1个好方法。

我正在编写一个用于模拟分布式传感器群的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

您可以在包中找到具有其他功能的模块.



1> cdleary..:

如果您想坚持原始的数据地图设计,可以使用合成来解决传感器架构.你似乎是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

您可以在包中找到具有其他功能的模块.

推荐阅读
wurtjq
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有