2017-04-18 5 views
0

私はかなり簡単な問題だと思っていますが、満足のいく答えを見つけることができませんでした。簡単に言うと、それぞれの子クラスに論理を追加することなく、親クラスの子クラスに対するコントラクトを強制したいのです。以下のコード例:Pythonの子クラスの装飾機能

class A(object): 

    @abc.abstractmethod 
    def do_thing(self, input): 
     raise NotImplementedError 

class A1(A): 

    def do_thing_decorator(self, do_thing_func): 
     def checked_do_thing(input): 
      check = do_thing_func(input) 
      if check != 1: 
       raise ValueError 
      return check 
     return checked_do_thing 

そこで問題は、どのように私はA1から継承したクラスで実装do_thing機能を自動的に飾るんですか? A2クラスと少し異なるチェックがあるとします。

私の最初の調査では、メタクラスが取るアプローチであるが、どのように動作するかについての大きな説明を見つけるのが難しいことを暗示しています。理想的には、Python 2.xで機能するものを探していますが、3.xのソリューションしかない場合、私は自分のコードベースを変更してうれしく思います。

+1

あなたは '__new __()'メソッドを見たことがありますか? –

+0

@ IgnacioVazquez-Abramsは不合理な解決策ではありません。私は、メソッドレベルでもう少し直感的なものを望んでいましたが、何か良いものが見つからなければ、おそらくデフォルトになります。 –

答えて

2

まず、abcモジュールを間違って使用しています(see the docs)。あなたのクラスAabc.ABCMetaをメタクラスとして持つべきです。したがって、既にメタクラスを使用している場合は、それをあなたの利点にまで拡張することができます。

abstractmethod仕事をするためにabc.ABCMetaから継承し、do_thingを飾るメタクラス:

from abc import ABCMeta, abstractmethod 

class DecoratingMeta(ABCMeta): 
    def __new__(cls, *args): 
     new_class = super(DecoratingMeta, cls).__new__(cls, *args) 
     # decorating do_thing manually 
     new_class.do_thing = new_class.do_thing_decorator(new_class.do_thing) 
     return new_class 

何もしない、デフォルトのチェックデコレータで今すぐあなたの抽象ベースクラス:

# class Abstract(metaclass=ABCMeta): in python3 
class Abstract(object): 
    __metaclass__ = DecoratingMeta # remove this line in python3 
    @abstractmethod 
    def do_thing(self, input): 
     pass 

    @classmethod 
    def do_thing_decorator(cls, function): 
     return function  # default decorator that does nothing 

do_thing_decoratorことこの場合、クラスメソッドでなければなりません。 python3およびpython2で動作するメタクラスについては、sixを参照してください。のみ、特定のチェッカーを実装していますが、まだ抽象的である

あなたのチェッカークラス:あなたはcheck = do_thing_func(input)を書いた行は再帰エラーになるだろうと

class Checker(Abstract): 
    @classmethod 
    def do_thing_decorator(cls, function): 
     def do_checked_thing(self, input): 
      check = function(self, input) # NOT self.do_thing(input) else recursion error 
      if check != 1: 
       raise ValueError("Check failed") 
      return check 
     return do_checked_thing 

注意。

そしてdo_thingのサンプル実装を使用して、具体的なクラス:

class Concrete(Checker): 
    def do_thing(self, input): 
     return input # sample implementation 

あなたはdo_thing(1)が成功したことを確認することができますし、do_thing(2)は、このアプローチの欠点は、あなたが作ることができないということです

c = Concrete() 

c.do_thing(1) 
try: 
    c.do_thing(2) 
except ValueError: 
    print("check failed") 

を失敗しました要約書。

だから、これはすでに大量のテキストだったが、あなたはまったくのメタクラスを使用しない場合は、はるかに簡単な方法があります:

はでdo_thing方法でチェックを実行するクラスを書きますdo_thing_funccheck_do_thingが本当に抽象的、あなたがタイプAbstractのまだインスタンス化するオブジェクトをできることではないことを

class Abstract(object): 
    def do_thing_func(self, input): 
     raise NotImplementedError() 

    def check_do_thing(self, result): 
     raise NotImplementedError() 

    # don't override this method 
    def do_thing(self, input): 
     result = self.do_thing_func(input) 
     self.check_do_thing(result) # may raise 
     return result # if it does not raise return the result 

注:2「抽象」メソッドを使用して。それらを抽象的にする必要がある場合は、標準abc.ABCMetaメタクラスをここで使用してください。

は、今、私たちはここにデコレータを必要としないので、これは非常に簡単になりcheck_do_thing

class Checker(Abstract): 
    def check_do_thing(self, result): 
     if result != 1: 
      raise ValueError("check failed") 

実装チェッカークラスを作成します。

そしてConcreteは今do_thing_funcを実装する必要がありますが、あなたはクラスを使用するときにdo_thingを呼び出す必要があることdo_thing_func

class Concrete(Checker): 
    def do_thing_func(self, input): 
     return input # sample implementation 

注意を実装して、最後に具体的なクラス。

ここでの短所は、do_thingを上書きしてチェックを破棄できることです。

関連する問題