2016-12-24 14 views
2

いくつかのクラスがabcクラス(抽象基本クラス)を拡張する場合、すべての抽象メソッドを定義しない限り、そのインスタンスをインスタンス化できません。しかし、しばしばデコレータパターンを実装する際に、私はいくつかの抽象メソッドしか定義しません。それ以外は装飾オブジェクトに委譲します。これを行う方法?例えばPython abcクラスを拡張するクラスの自動委譲

、私は次のコードの仕事をしたい:

from abc import ABCMeta, abstractmethod 


class IElement(object): 
    __metaclass__ = ABCMeta 

    @abstractmethod 
    def click(self): 
     return 

    @abstractmethod 
    def hover(self): 
     return 

    # ... more IElement's abstractmethods... 


class StandardElement(IElement): 

    def click(self): 
     return "click" 

    def hover(self): 
     return "hover" 

    # ... more implemented IElement's methods... 


class MyElement(IElement): 
    def __init__(self, standard_element): 
     self._standard_element = standard_element 
     delegate(IElement, standard_element) 

    def click(self): 
     return "my click" 


assert MyElement(StandardElement()).click() == 'my click' 
assert MyElement(StandardElement()).hover() == 'click' 

の代わり

このため
from abc import ABCMeta, abstractmethod 


class IElement(object): 
    __metaclass__ = ABCMeta 

    @abstractmethod 
    def click(self): 
     return 

    @abstractmethod 
    def hover(self): 
     return 

    # ... more IElement's abstractmethods... 


class StandardElement(IElement): 

    def click(self): 
     return "click" 

    def hover(self): 
     return "hover" 

    # ... more implemented IElement's methods... 


class MyElement(IElement): 
    def __init__(self, standard_element): 
     self._standard_element = standard_element 

    def click(self): 
     return "my click" 

    def hover(self): 
     return self._standard_element.hover() 

    # ... more manually delegated IElement's methods to self._standard_element object, aggregated in initialiser... 


assert MyElement(StandardElement()).click() == 'my click' 
assert MyElement(StandardElement()).hover() == 'click' 

私は上記の例からdelegateメソッドを実装する必要があります。それを実装する方法は? abcクラスを拡張するクラスに対して自動委任を提供するための他のアプローチも考えられます。

P.S. ここに解決策として継承(class MyElement(StandardElement))を提案しないでください...上記のコードは単なる例です。私の実際のケースでは、MyElementはStandardElementと比べてかなり異なっています。それでも、StandardElementの代わりにMyElementを使用することがあるため、MyElementをStandardElementと互換性を持たせる必要があります。私は実際には "ここで"関係を持っている必要があります。

+0

「StandardElement」は抽象クラスであると考えられていますか? – Fomalhaut

+0

No.StandardElementはIElementの実装です。 IElementは抽象クラスです... – yashaka

答えて

0

クラスMyElementが抽象であるため(hoverのメソッドをオーバーライドしていないため)、このエラーが発生します。抽象クラスは、少なくとも1つの抽象メソッドを持つクラスです。エラーを解決するためには、例えば、このように、このメソッドを追加する必要があります

class MyElement(IElement): 
    def __init__(self, standard_element): 
     self._standard_element = standard_element 

    def click(self): 
     return "my click" 

    def hover(self): 
     return self._standard_element.hover() 

をあなたはabc.abstractmethod経由不可能であるように思わ__getattr__を使用して多くの方法を委任する場合、__getattr__が動的に必要な方法を探しているため、その__getattr__を直接実行することなく検査することは不可能です。

したがって、すべてのメソッドを抽象クラス(@abstractmethodを介して)で指定することをお勧めします。これは確実に各継承クラスでオーバーライドされ、NotImplementedErrorを使用して委譲されたメソッドを実装します。例:

class IElement(object): 
    __metaclass__ = ABCMeta 

    @abstractmethod 
    def necessary_to_implement_explicitely(self): 
     pass 

    def click(self): 
     raise NotImplementedError() 

    def hover(self): 
     raise NotImplementedError() 
+0

あなたは大歓迎です。あなたの問題を解決しましたか? – Fomalhaut

+0

いいえ:)このような「古典的な」委任の実装でも:https://www.refheap.com/124379。そして、これは明らかです。すべての抽象メソッドがオーバーライドされているわけではないが、abcディセーブルを使用してクラスをインスタンス化するので、うまくいきません – yashaka

+0

さて、今あなたが意味するものがあります。私は数分で私の答えを編集します。 – Fomalhaut

3

デフォルトで、委任を自動的に行う方法はありません。委任は実際に抽象クラスの明示的な部分ではないので、それは本当に驚きではありません。私たちは関数が作成されています後nameが変化しない名前空間を必要とするので、委任者の方法は、別の関数で作られています

def _make_delegator_method(name): 
    def delegator(self, *args, **kwargs): 
     return getattr(self._delegate, name)(*args, **kwargs) 
    return delegator 

class DelegatingMeta(ABCMeta): 
    def __new__(meta, name, bases, dct): 
     abstract_method_names = frozenset.union(*(base.__abstractmethods__ 
                for base in bases)) 
     for name in abstract_method_names: 
      if name not in dct: 
       dct[name] = _make_delegator_method(name) 

     return super(DelegatingMeta, meta).__new__(meta, name, bases, dct) 

:あなたは、しかし、あなたのために不足しているメソッドを追加して、独自の委譲メタクラスを書くことができます。 Python 3では、delegatorにデフォルト値のキーワードのみの引数を与えることで、__new__メソッドですべてのことを実行できますが、Python 2ではキーワードのみの引数がないため、うまく動作しません。ここで

は、あなたがそれを使用したい方法は次のとおりです。

self._delegateセットメタクラスによって作成されたメソッドが使用するオブジェクトに割り当て
class MyElement(IElement): 
    __metaclass__ = DelegatingMeta 

    def __init__(self, standard_element): 
     self._delegate = standard_element 

    def click(self): 
     return "my click" 

。あなたが望むなら、あなたはそれをある種の方法にすることができますが、これは最も簡単なアプローチであるようです。

+0

Mmm ...それは私が必要なもののようです:)私の元の例から、ある種の 'delegate'メソッドであなたのソリューションをラップすることができます。ありがとうございました!私はそれを使用しようとします。 – yashaka

+0

これはメタクラスを使用する必要があります。インスタンスが作成された後にのみ '__init__'が実行され、抽象クラスをインスタンス化できないため、' __init__'で 'delegate'メソッドを呼び出すことはできません。メタクラスを使用すると、クラスを作成する前にデリゲーターメソッドを追加できます。これにより、クラスのインスタンス化が可能になります。次に、デリゲーターメソッドが使用するオブジェクトの属性を設定します。デリゲーターが 'delegate()'関数またはメソッドに含めることができることを確認してください。しかし、おそらく必要はありません(単に属性を設定する方が簡単です)。 – Blckknght

+0

ああ、私はこれを書いて以来、複数の継承の処理が間違っている可能性があるので、私には起こっています。単一の継承のために書かれたとおりに動作し、抽象メソッドに関して異なる基底が決して相互作用しない多重継承のために書かれているように動作するはずです。ある基盤が別の基盤に具体的に実装されたまだ抽象的な方法を持っている状況では、それは間違っています。問題を解決することは不可能ではありませんが、私は今のところ時間があります。 – Blckknght

関連する問題