Обсуждение множественного наследования по сравнению с Составом для проекта (+other вещи)

Для объектов неизменяемых значений, таких как int, строки или даты, идентификация объекта не особенно полезна. Лучше думать о равенстве. Идентификация - это, по сути, деталь реализации объектов значения - поскольку они неизменяемы, нет эффективной разницы между наличием нескольких ссылок на один и тот же объект или несколько объектов.

14
задан Mike Hamer 14 March 2009 в 08:24
поделиться

3 ответа

Архитектура датчика может быть решена при помощи состава, если Вы хотите придерживаться своего исходного дизайна карты данных. Вы, кажется, плохо знакомы с 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()

, Который должен дать Вам общее представление об альтернативах твердой системе типов. Обратите внимание, что Вы не должны полагаться на иерархию типа вообще - просто реализуют к (потенциально неявному) единому интерфейсу.

МЕНЕЕ СТАРЫЙ:

После чтения вопроса более тщательно, я вижу, что то, что Вы имеете, является классическим примером ромбовидное наследование , который является зло, которое заставляет людей сбежать к единичному наследованию.

Вы, вероятно, не хотите это для начала, начиная с приседания средств иерархии классов в Python. То, что Вы хотите сделать, делают SensorInterface (минимальные требования для датчика) и имеют набор "mixin" классов, которые имеют полностью независимую функциональность, которая может быть вызвана через методы различных имен. В Вашей платформе датчика Вы не должны говорить вещи как isinstance(sensor, PositionSensor) - необходимо ли сказать, что вещи как "этот датчик могут определить геолокацию?" в следующей форме:

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

Это - основа вводящей утку философии и EAFP (Легче Попросить Прощение, чем Разрешение), оба из которых язык Python обнимается.

необходимо, вероятно, описать, какие методы эти датчики на самом деле реализуют так, мы можем описать, как можно использовать смешивание классов для сменной архитектуры.

СТАРЫЙ:

, Если они пишут код в модуле, который вставляется в сменный блок или что имеет Вас, можно волшебно оснастить классы для них при импорте их сменных модулей. Что-то вроде этого (непротестированного) отрывка:

 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

Вы можете находить модули в пакете с другой функцией.

11
ответ дан 1 December 2019 в 14:33
поделиться

super вызовы следующий класс в mro-списке. Это работает, даже если Вы не учитываете __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!
>>> 

Редактирование: Переписал ответ. (снова)

1
ответ дан 1 December 2019 в 14:33
поделиться

Вот частичное решение:

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__ код от подклассов Узла в некоторый другой метод (я использовал cls_init).

Редактирование: я отправил это, прежде чем я видел Ваши обновления; я перечитаю Ваш вопрос, и при необходимости, обновлю это решение.

1
ответ дан 1 December 2019 в 14:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: