1

私は、__mul____add__のような特別なメソッドを実装し、多重継承を使用する代数オブジェクトのクラスの階層を持っています。私は何とかPython(> = 3.5)が、NotImplementedを返さない最初のメソッドを見つけるためにメソッド解決命令(mro)を歩くと仮定していました。悲しいかな、これはそうではないようです。以下の最小限の例を考えてみます。このコードでNotImplementedを返すPython特別メソッドのウォークMRO

class A(): 
    def __mul__(self, other): 
     return "A * %s" % other 

class B(): 
    def __mul__(self, other): 
     if isinstance(other, int): 
      return "B * %s" % other 
     else: 
      return NotImplemented 

class C(B, A): 
    pass 

class D(B, A): 
    def __mul__(self, other): 
     res = B.__mul__(self, other) 
     if res is NotImplemented: 
      res = A.__mul__(self, other) 
     return res 

を、私は希望の動作とDを実装している:

>>> d = D() 
>>> d * 1 
'B * 1' 
>>> d * "x" 
'A * x' 

をしかし、私は実際にDと同じように動作するCを期待しているだろう、それdoes not:

>>> c = C() 
>>> c * 1 
'B * 1' 
>>> c * "x" 
Traceback (most recent call last): 
File "<ipython-input-23-549ffa5b5ffb>", line 1, in <module> 
    c * "x" 
TypeError: can't multiply sequence by non-int of type 'C' 

私は何が起こっているのか理解しています:私はちょうどfiの結果を返しています(私はちょうどNotImplementedが特別な値として扱われることを望んでいた)

D.__mul__のような定型文を書く方法があれば私の質問です。すべてのクラスのためのすべての数値的特殊メソッド)。私はこれらのメソッドを自動的に生成するためにクラスデコレータやメタクラスを書くことができると思いますが、もっと簡単な(標準ライブラリ)方法があるとか、誰かがすでにこのようなことをしていることを望んでいました。

答えて

1

あなたがそれを求めるときにPythonがMROを歩いていると、それ以上のチェックを続けることは暗黙のことではありません。 NotImplementedを返す場合は、super()(MROを次のクラスに移動する要求)で協力的継承を使用するようにコードを変更してください。彼らはその機能には何も追加しないので、それは、全く__mul__を定義するためのCまたはDのいずれかの必要性を削除します。

class A(): 
    def __mul__(self, other): 
     return "A * %s" % other 

class B(): 
    def __mul__(self, other): 
     if isinstance(other, int): 
      return "B * %s" % other 
     try: 
      return super().__mul__(other) # Delegate to next class in MRO 
     except AttributeError: 
      return NotImplemented # If no other class to delegate to, NotImplemented 

class C(B, A): 
    pass 

class D(B, A): 
    pass # Look ma, no __mul__! 

次にテスト:

>>> d = D() 
>>> d * 1 
'B * 1' 
>>> d * 'x' 
'A * x' 

super()の魔法はそのことです1つのクラスBはこの場合Aについて何も知らないが、子が両方から継承した場合でもそれを(または他の利用可能なクラスに)喜んで委譲する複数の継承シナリオでも機能します。そうでない場合は、我々は以前のように、結果NotImplementedを作るために結果AttributeErrorを扱うので、予想通り、このようなものは、(それが非intと爆発を認識しないstr__rmul__をしようとします)作品:

>>> class E(B): pass 
>>> e = E() 
>>> e * 1 
'B * 1' 
>>> e * 'x' 
Traceback (most recent call last) 
... 
TypeError: can't multiply sequence by non-int of type 'E' 
+0

うん、それだよ!あなたが一度それを見たら完全に意味をなさない! –

関連する問題