Pythonでは、スーパークラスが1回限りのAngimeTupleである場合、どのようにスーパークラスを呼び出すことができますか?

StackOverflow https://stackoverflow.com/questions/4071765

質問

そのため、シリアル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 電話から、それはまだ機能しているようですが、なぜですか?コンストラクターに対するこれらの議論は、どのようにしてAndameTupleに渡されますか?これは保証されていますか、それともどのようにランダムな結果が得られますか mro 確立されますか?

私がスーパーから離れて、それを古い方法でやりたいと思ったら、そのコンストラクターを呼び出すために名前を付けたものにアクセスできる方法はありますか?私はこれをする必要はありません:

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

一種の冗長で不必要なようです。

ここで私の最高の行動方針は何ですか?

役に立ちましたか?

解決

手始めに、 namedtuple(whatever) 継承します tuple, 、これは不変で、不変のタイプは気にしません __init__, 、その時までに __init__ オブジェクトはすでに構築されています。引数をに渡したい場合 namedtuple ベースクラスオーバーライドする必要があります __new__ 代わりは。

の結果の定義を見ることができます namedtuple() aを渡すことによって verbose=true 口論;私はそれが教育的だと思います。

他のヒント

3つの基本クラスがあります。 Payload, 、あなたの名前を付けました DifferentialSpeed_, 、および共通の基本クラス object. 。最初の2つはどちらもありません __init__ 継承されたものを除いて、まったく機能します object. namedtuple 必要ありません __init__, 、不変のクラスの初期化は __new__, 、前に呼ばれます __init__ 実行されています。

以来 super(DifferentialSpeed, self).__init__ 次へと解決します __init__ コールチェーンでは、次は __init__object.__init__, 、それはあなたがその関数に議論を渡していることを意味します。それは何も期待していません - 議論を渡す理由はありません object.__init__.

(以前は議論を受け入れて無視していました。その行動はなくなっています。Python3ではなくなりました。これが、非推奨を得る理由です。)

追加することにより、問題をより明確にトリガーできます Payload.__init__ 引数を取得しない関数。あなたが `*を伝えようとするときKwargs, 、エラーが発生します。

この場合に行うべき正しいことは、ほぼ確実に削除することです **kwargs 議論、そしてただ電話してください super(DifferentialSpeed, self).__init__(). 。議論は必要ありません。 DifferentialSpeed 通過しています Payload これは 自分の その議論 Payload, 、そしてコールチェーンのさらに下に機能し、何も知りません。

他の人が指摘したように、タプルは不変のタイプであり、彼らの中で初期化する必要があります __new__() 彼らの代わりに __init__() 方法 - したがって、前者をサブクラスに追加する必要があります(そして後者を取り除きます)。以下は、これがサンプルコードに適用される方法です。他の唯一の変更は、aを追加することでした from import... 最初への声明。

ノート: cls で2回渡す必要があります super() 呼び戻す __new__() それは静的な方法だからですが、それは特別なケースであるため、それが1つであると宣言する必要はありません。

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)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top