Для объектов неизменяемых значений, таких как int, строки или даты, идентификация объекта не особенно полезна. Лучше думать о равенстве. Идентификация - это, по сути, деталь реализации объектов значения - поскольку они неизменяемы, нет эффективной разницы между наличием нескольких ссылок на один и тот же объект или несколько объектов.
Архитектура датчика может быть решена при помощи состава, если Вы хотите придерживаться своего исходного дизайна карты данных. Вы, кажется, плохо знакомы с 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
Вы можете находить модули в пакете с другой функцией.
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!
>>>
Редактирование: Переписал ответ. (снова)
Вот частичное решение:
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
).
Редактирование: я отправил это, прежде чем я видел Ваши обновления; я перечитаю Ваш вопрос, и при необходимости, обновлю это решение.