Pythonでは、スーパークラスが1回限りのAngimeTupleである場合、どのようにスーパークラスを呼び出すことができますか?
-
28-09-2019 - |
質問
そのため、シリアル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)