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

时间:2009-03-14 06:04:55

标签: python constructor oop multiple-inheritance

3 个答案:

答案 0 :(得分: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和代理(通过__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()

这应该让您了解刚性类型系统的替代方案。请注意,您根本不必依赖于类型层次结构 - 只需实现(可能是隐式的)公共接口。

LESS OLD:

在仔细阅读问题后,我发现你所拥有的是diamond inheritance的典型例子,它是单{{}}的evil that makes people flee

你可能不希望这开始,因为类层次结构意味着在Python中蹲下。你想要做的是做一个SensorInterface(传感器的最低要求),并有一堆“mixin”类,它们具有完全独立的功能,可以通过各种名称的方法调用。在您的传感器框架中,您不应该说isinstance(sensor, PositionSensor)之类的内容 - 您应该说“这个传感器可以进行地理定位吗?”采用以下形式:

def get_position(sensor):
    try:
        return sensor.geolocate()
    except AttributeError:
        return None

这是鸭子类型哲学的核心,EAFP(比宽容更容易要求宽恕),这两者都是Python语言所包含的。

您应该描述这些传感器实际实现的方法,以便我们可以描述如何将mixin类用于插件架构。

OLD:

如果他们将代码写入一个放在插件包中的模块或者你有什么,你可以在导入他们的插件模块时神奇地为它们设置类。这段代码的一些内容(未经测试):

 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

您可以find the modules within a package使用其他功能。

答案 1 :(得分:1)

super调用mro-list中的下一个类。即使你遗漏了__init__形式的某个类,这也行得正常。

class A(object):
  def __init__(self):
    super(A,self).__init__()
    print "Hello from A!"

class B(A):
  def __init__(self):
    super(B,self).__init__()
    print "Hello from B!"

class C(A):
  def __init__(self):
    super(C,self).__init__()
    print "Hello from C!"

class D(B,C):
  def __init__(self):
    super(D,self).__init__()
    print "Hello from D!"

class E(B,C):
  pass

示例:

>>> x = D()
Hello from A!
Hello from C!
Hello from B!
Hello from D!
>>> y = E()
Hello from A!
Hello from C!
Hello from B!
>>> 

编辑:重写了答案。 (再次)

答案 2 :(得分:1)

这是部分解决方案:

class NodeMeta(type):
    def __init__(cls, name, bases, d):
        setattr(cls, '__inherits__', bases)

class Node(object):
    __metaclass__ = NodeMeta

    def __init__(self):
        for cls in self.__inherits__:
            cls.cls_init(self)

class Sensor(Node):
    def cls_init(self):
        print "Sensor initialized"

class PositionSensor(Sensor):
    def cls_init(self):
        print "PositionSensor initialized"
        self._bearing = 0

    def bearing(self):
        # calculate bearing:
        return self._bearing

class BearingSensor(Sensor):
    def cls_init(self):
        print "BearingSensor initialized"
        self._position = (0, 0)

    def position(self):
        # calculate position:
        return self._position

# -------- custom sensors --------

class CustomSensor(PositionSensor, BearingSensor):
    def think(self):
        print "Current position:", self.position()
        print "Current bearing:", self.bearing()

class CustomSensor2(PositionSensor, BearingSensor, Sensor):
    pass

>>> s = CustomSensor()
PositionSensor initialized
BearingSensor initialized
>>> s.think()
Current position: (0, 9)
Current bearing: 0

您必须将__init__代码从Node子类移动到其他方法(我使用cls_init)。

修改:在我看到您的更新之前,我发布了这个内容;我会重新阅读您的问题,如有必要,请更新此解决方案。