2016-08-22 9 views
3

フレームワークでは、フレームワークユーザーのサブクラスである基本クラスを提供したいことがよくあります。基本クラスは、基本クラスへの制御されたアクセスを提供します。これを実現する一つの方法は、接頭辞としてアンダースコアを追加することによって、例えば、異なる名前で実装されていない方法を提供することである。継承ツリーに子メソッドを複数回ラップする方法は?

class Base: 

    def method(self, arg): 
     # ... 
     result = self._method(arg) 
     # ... 
     return result 

    def _method(self, arg): 
     raise NotImplementedError 

しかし、この方式では唯一の継承の1つのレベルで動作します。より多くのレベルでは、メソッド名の違いによって、何が起こっているのかを把握することが難しくなります。また、フレームワークの利用者は、彼が選択したベース・クラスに応じて異なる方法でオーバーライドすることがあります。

class Base: 

    def method(self, arg): 
     # ... 
     result = self._method_sub(arg) 
     # ... 
     return result 

    def _method_sub(self, arg): 
     raise NotImplementedError 

class Intermediate(Base): 

    def _method_sub(self, arg): 
     # ... 
     result = self._method_sub_sub(arg) 
     # ... 
     return result 

    def _method_sub_sub(self, arg): 
     raise NotImplementedError 

を基本メソッドは、子メソッドの戻り値にアクセスする必要があるときに、スーパーメソッドを呼び出すことは助けにはなりません。オブジェクトの向きに若干の欠陥があると感じ、子クラスにコールを転送できるようにするchildキーワードがありません。どのような解決策がこの問題を解決するために存在する?

+0

@FujiApple Pythonに最終的なメソッドがあるとしましょう。どのように子クラスにそれらの一部を実装させることができますか?私の例のメソッドは本当に最終的なものではありません。子クラスは動作を提供し、基本クラスはそれをラップします。基本メソッドは、引数や戻り値に振る舞いや範囲チェックを追加することがあります。 – danijar

+0

私は質問を理解するのに苦労しています。私はあなたが望むものを知っているかもしれませんが、私は本当にわかりません: 'Base.method()'が 'Base.methodSub()'をラップし、 methodSub() 'の実装です。親 'Base.method()'は 'methodSub()'の子実装の前後の検証を行うことができます。それが正しいと思われる場合は、例をノックアップすることができます私は弓を切って誰かがそれを把握できるように:) – FujiApple

+0

@FujiAppleまったく。しかし、 'Base'と' Intermediate'はどちらもフレームワークが提供する抽象クラスです。フレームワークユーザが 'Intermediate'を拡張するためには、クラスは' Intermediate.methodSubSub() 'を提供しなければなりません。フレームワークのユーザは、自分が選んだ基底クラスに応じて、異なるメソッドをオーバーライドする必要があるので、これはうまくありません。 – danijar

答えて

1

質問は、中間クラスのビヘイビア拡張が発生する可能性があるさまざまなポイントに焦点を当てています。中間階級は明らかに "コントロール"の部分を改良しなければならない。

第一ソリューション

ほとんどは、これは単なる「安全な」メソッドをオーバーライドすることで、古典的な方法で行うことができます - 「ベースと中間の両方が、フレームワーク提供する抽象クラスである」場合には特に、物事はそう整理することができます。 スペード作業を行う最終的な "愚かな"実装クラスは、安全でないメソッドをオーバーライドします。この例の

思う:

class DoublePositive: 
    def double(self, x): 
     assert x > 0 
     return self._double(x) 
    def _double(self, x): 
     raise NotImplementedError 

class DoubleIntPositive(DoublePositive): 
    def double(self, x): 
     assert isinstance(x, int) 
     return DoublePositive.double(self, x) 

class DoubleImplementation(DoubleIntPositive): 
    def _double(self, x): 
     return 2 * x 

第二ソリューション

は、非古典的な方法で、「内側」の実行ポイントでこのように行動延長、仮想子クラスのメソッドを呼び出すと、可能性Pythonでのイントロスペクションによって行うことができます。クラス__bases__またはメソッドの分解手順__mro__をヘルパー関数で実行します。

例:

def child_method(cls, meth, _scls=None): 
    scls = _scls or meth.__self__.__class__ 
    for base in scls.__bases__: 
     if base is cls: 
      cmeth = getattr(scls, meth.__name__, None) 
      if cmeth.__func__ is getattr(cls, meth.__name__, None).__func__: 
       return child_method(scls, meth) # next child 
      if cmeth: 
       return cmeth.__get__(meth.__self__) 
    for base in scls.__bases__: 
     r = child_method(cls, meth, base) # next base 
     if r is not None: 
      return r 
    if _scls is None: 
     raise AttributeError("child method %r missing" % meth.__name__)  
    return None 

class Base(object): 
    def double(self, x): 
     assert x > 0 
     return Base._double(self, x) 
    def _double(self, x): 
     return child_method(Base, self._double)(x)   
class Inter(Base): 
    def _double(self, x): 
     assert isinstance(x, float) 
     return child_method(Inter, self._double)(x) 
class Impl(Inter): 
    def _double(self, x): 
     return 2.0 * x 

ここでヘルパー関数child_method()は一種の反対Pythonのsuper()のことです。


第三ソリューション

呼び出しが柔軟にチェーン可能にする必要がある場合は、物事は、明示的にハンドラチェーンの一種として整理することができます。 __init__()チェーンのself.addHandler(self.__privmeth)、またはトリッキーなメタクラスを介して考えることもできます。例: urllib2ハンドラチェーン。

+0

あなたの答えをありがとう。あなたの最初の解決策は非常にきれいですが、 '中級 'は' Base'の動作を拡張できないことを意味します。基本クラスが実装されていないユーザメソッドを呼び出すので、 'super()'を呼び出すことはできません。あなたはこれを解決する方法を考えることができますか?あなたは2番目の解決策が実際に働くでしょう。私は継承の周りで働かないことを望みましたが、それが唯一の方法なら、私はそれを受け入れます。 – danijar

+0

最初の解決策では、中間クラス(抽象クラ​​スも同様)は基本動作を "* extend *"します。他のポイントでは、基本クラスを呼び出す前と後で処理を行うことで "外側"になります。 ( ' .double()'はここでは明示的な 'super()'呼び出しです)。イントロスペクションヘルパー関数を介した子メソッド呼び出しの解決策は、他の点でも "inside"です。 両方のソリューションと中間クラスでデフォルトの実装を提供するオプションの両方を、中間クラスで混在させることもできます:中間クラスが安全で安全でないメソッドをオーバーライドする場合。それで、4つのポイントで行動を拡張することができます。 – kxr

+0

答えを明確にして更新してくれてありがとう! – danijar

2

これはあなたに何を望みますか?

+0

ビルダーや抽象的な工場設計パターンのようなものですか? – Vipul

+1

私はそれが[テンプレートメソッド](https://en.wikipedia.org/wiki/Template_method_pattern)GoFパターンに最も近いと思います。 – FujiApple

+0

これはかなり解決策です。親メソッドは子メソッドの戻り値を取得できないので、それを別のメソッドに委譲するだけです。 –

関連する問題