14

分散センサー群のシミュレーション用の 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() など)。

私は当初、より技術的なユーザーが必要に応じて既存のクラスの 1 つを簡単に拡張して新しいセンサーを作成できるように、(共通の祖先を持つ) センサーに個別のクラスを使用したいと考えていました。例えば:

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() に解決することもできません。継承を使用して実装されます。

センサーに関しては、このような問題に対する構成の利点は明らかです。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 型のオブジェクトを構築する場合、スーパークラスのすべてのコンストラクターを呼び出す必要があります。各スーパークラスからコンストラクターを呼び出す 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)

洞察や一般的なコメントをいただければ幸いです、乾杯:)

古い編集:次のようなことをしています:

#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 のインスタンスであるため、これは機能します。ただし、このソリューションは面倒です。


4

3 に答える 3

12

元のデータのマップ設計に固執したい場合は、合成を使用してセンサー アーキテクチャを解決できます。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

アクチュエータで説明しているアーキテクチャ上の問題は、継承ではなく、ミックスインとプロキシ(を介して) を使用することで解決されます。__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(センサーの最小要件)を作成し、さまざまな名前のメソッドを介して呼び出すことができる完全に独立した機能を持つ「ミックスイン」クラスを多数用意することです。センサー フレームワークでは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

別の機能を持つパッケージ内のモジュールを見つけることができます。

于 2009-03-14T12:11:31.143 に答える
1

supermro-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!
>>> 

編集:答えを書き直しました。(また)

于 2009-03-14T07:44:16.437 に答える
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ました)。

編集:更新を見る前にこれを投稿しました。あなたの質問を読み直し、必要に応じてこのソリューションを更新します。

于 2009-03-14T09:14:50.200 に答える