gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 17:18:46 25 4
gpt4 key购买 nike

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

新编辑:

首先概述我想要实现的目标:
我正在编写一个模拟器来模拟群体智能算法,特别关注移动传感器网络。这些网络由许多小型机器人组成,它们通信各个传感器数据,以构建复杂的环境传感图。

该项目的基本目标是开发一个模拟平台,为传感器提供抽象接口(interface),以便相同的用户实现功能可以直接移植到运行嵌入式 linux 的机器人群。由于机器人实现是目标,我需要设计成软件节点的行为相同,并且只能访问物理节点将拥有的信息。

作为模拟引擎的一部分,我将提供一组对不同类型的传感器和不同类型的传感器节点进行建模的类。我希望从用户那里抽象出所有这些复杂性,以便用户必须做的就是定义节点上存在哪些传感器,以及正在实现的传感器节点(移动、固定位置)的类型。

我最初的想法是每个传感器都会提供一个 read() 方法来返回相关值,但是在阅读了对问题的回答后,我发现可能更具描述性的方法名称会更好(.distance(), .position() )、.bearing() 等)。

我最初希望为传感器使用单独的类(具有共同的祖先),以便技术性更强的用户可以轻松扩展现有类之一,如果他们愿意的话,可以创建一个新的传感器。例如:
Sensor
|
DistanceSensor(designed for 360 degree scan range)
| | |
IR Sensor Ultrasonic SickLaser
(narrow) (wider) (very wide)

我最初考虑多重继承的原因(虽然它半打破了继承的 IS-A 关系)是由于模拟系统背后的基本原理。让我解释:

用户实现的 MySensorNode 不应该直接访问其在环境中的位置(类似于机器人,访问是通过传感器接口(interface)间接访问的),同样,传感器不应该知道它们在哪里。然而,缺乏直接知识会带来问题,因为传感器的返回值都取决于它们在环境中的位置和方向(需要模拟以返回正确的值)。

SensorNode 作为在模拟库中实现的类,负责在 pygame 环境中绘制 MySensorNode - 因此,它是唯一可以直接访问环境中传感器节点的位置和方向的类。

SensorNode 还负责环境内的平移和旋转,但是这种平移和旋转是电机驱动的副作用。

我的意思是机器人不能直接改变他们在世界中的位置,他们所能做的就是为电机提供动力,而世界内的运动是电机与环境相互作用的副作用。我需要在模拟中准确地对此进行建模。

因此,要移动,用户实现的功能可能会使用:
motors(50,50)

作为副作用,此调用将改变节点在世界中的位置。

如果 SensorNode 是使用组合实现的,SensorNode.motors(...) 将无法直接更改实例变量(例如位置),MySensorNode.draw() 也不会解析为 SensorNode.draw(),因此 SensorNode imo 应该使用继承来实现。

就传感器而言,组合对于此类问题的好处是显而易见的,MySensorNode 由多个传感器组成 - 足够了。

然而,我看到的问题是传感器需要访问它们在世界中的位置和方向,如果你使用组合,你最终会得到一个像这样的调用:
>>> PosSensor.position((123,456))
(123,456)

再说一遍 - 思考,您可以在初始化时将 self 传递给传感器,例如:
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 类型的对象时,需要调用来自父类(super class)的所有构造函数。我不想让用户不得不为 MySensorNode 编写一个自定义构造函数,它从每个父类(super class)调用构造函数。理想情况下,我希望发生的是:
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)

任何见解或一般评论将不胜感激,
干杯:)

旧编辑:
做类似的事情:
#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 的一个实例。然而,这个解决方案是困惑的。

最佳答案

如果您想坚持原始数据 map 设计,可以通过使用组合来解决传感器架构。您似乎是 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__ )而不是继承。 (代理可以是继承的一个很好的替代方案,因为要代理的对象可以在运行时绑定(bind)/取消绑定(bind)。此外,您不必担心使用此技术在单个构造函数中处理所有初始化。)
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()

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

不那么旧:

仔细阅读问题后,我看到你所拥有的是 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 类。

老的:

如果他们将代码编写在放入插件包或您拥有的模块中,您可以在导入他们的插件模块时神奇地为他们检测类。类似于此代码段的内容(未经测试):
 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与另一个功能。

关于python - 项目的多重继承与组合的讨论(+其他事情),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/645493/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com