15

そのため、シリアル API 用の多数のメッセージ ペイロード クラスがあり、それぞれに多数の不変フィールド、解析メソッド、および共有されるいくつかのメソッドがあります。私がこれを構築している方法は、それぞれがフィールドの動作の名前付きタプルから継承し、親クラスから共通のメソッドを受け取ることです。ただし、コンストラクターにはいくつかの問題があります。

class Payload:
    def test(self):
        print("bar")

class DifferentialSpeed(Payload, namedtuple('DifferentialSpeed_', 
    'left_speed right_speed left_accel right_accel')):
    __slots__ = ()
    def __init__(self, **kwargs):
        super(DifferentialSpeed, self).__init__(**kwargs)
        # TODO: Field verification
        print("foo")

    @classmethod
    def parse(self, raw):
        # Dummy for now
        return self(left_speed = 0.0, right_speed = 0.1,
                    left_accel = 0.2, right_accel = 0.3)

    def __str__(self):
        return "Left Speed: %fm/s\nRight Speed: %fm/s\n"\
            "Left Acceleration: %fm/s^2\nRight Acceleration: %fm/s^2" % (
            self.left_speed, self.right_speed, self.left_accel, self.right_accel)


payload = DifferentialSpeed.parse('dummy')
print(payload)

これは機能しますが、次の警告が表示されます。

DeprecationWarning: object.__init__() takes no parameters
  super(DifferentialSpeed, self).__init__(**kwargs)

通話から外しても機能**kwargsしているように見えますが、なぜですか? コンストラクターへのこれらの引数は、namedtuple にどのように渡されますか? これは保証されていますか、それとも mroが確立される方法のランダムな結果ですか?

スーパーから離れて古い方法でそれを行いたい場合、namedtuple にアクセスしてそのコンストラクターを呼び出す方法はありますか? 私はむしろこれを行う必要はありません:

DifferentialSpeed_ = namedtuple('DifferentialSpeed_', 
    'left_speed right_speed left_accel right_accel')
class DifferentialSpeed(Payload, DifferentialSpeed_):

冗長で不必要なようです。

ここでの私の最善の行動は何ですか?

4

3 に答える 3

28

まず第一に、不変であるnamedtuple(whatever)から継承しtuple、不変の型は を気にしませ__init____init__. 基本クラスに引数を渡したい場合は、代わりnamedtupleにオーバーライドする必要があります。__new__

引数namedtuple()を渡すことで、 の結果の定義を確認できます。verbose=true教育的だと思います。

于 2010-11-01T18:17:00.960 に答える
5

Payload、namedtuple DifferentialSpeed_、および共通の基本クラスの3 つの基本クラスがありますobject__init__から継承されたものを除いて、最初の 2 つの関数はまったくありませんobject。 不変クラスの初期化は、実行前に呼び出されるによって行われるため、namedtupleは必要ありません。__init____new____init__

は呼び出しチェーンsuper(DifferentialSpeed, self).__init__の次のものに解決されるため、次は です。これは、その関数に引数を渡していることを意味します。に引数を渡す理由はありません。__init____init__object.__init__object.__init__

(以前は、引数を受け入れて静かに無視していました。その動作はなくなります。Python 3 ではなくなりました。これが DeprecationWarning を受け取る理由です。)

Payload.__init__引数を取らない関数を追加することで、問題をより明確にトリガーできます。`* kwargsを渡そうとすると、エラーが発生します。

この場合に行うべき正しいことは、ほとんどの場合、**kwargs引数を削除して、単に を呼び出すことsuper(DifferentialSpeed, self).__init__()です。引数は必要ありません。は、呼び出しチェーンのさらに下にある 、および関数が何も知らない独自の引数をDifferentialSpeed渡しています。PayloadPayload

于 2010-11-01T18:26:26.017 に答える
3

他の人が指摘したように、タプルは不変の型であり、メソッドでは__new__()なくその中で初期化する必要があります。その__init__()ため、前者をサブクラスに追加する必要があります (後者は削除します)。以下は、これをサンプルコードに適用する方法です。from import...その他の唯一の変更点は、冒頭にステートメントを追加したことです。

注: これは静的メソッドであるため、呼び出しでcls2 回渡す必要がありますが、特殊なケースであるため、1 つとして宣言する必要はありません。super()__new__()

from collections import namedtuple

class Payload:
    def test(self):
        print("bar")

class DifferentialSpeed(Payload, namedtuple('DifferentialSpeed_',
    'left_speed right_speed left_accel right_accel')):
    #### NOTE: __new__ instead of an __init__ method ####
    def __new__(cls, **kwargs):
        self = super(DifferentialSpeed, cls).__new__(cls, **kwargs)
        # TODO: Field verification
        print("foo")
        return self

    @classmethod
    def parse(self, raw):
        # Dummy for now
        return self(left_speed = 0.0, right_speed = 0.1,
                    left_accel = 0.2, right_accel = 0.3)

    def __str__(self):
        return "Left Speed: %fm/s\nRight Speed: %fm/s\n"\
            "Left Acceleration: %fm/s^2\nRight Acceleration: %fm/s^2" % (
            self.left_speed, self.right_speed, self.left_accel, self.right_accel)


payload = DifferentialSpeed.parse('dummy')
print(payload)
于 2010-11-01T20:40:43.503 に答える