4

一部のクラスがabcクラス(抽象基本クラス)を拡張する場合、すべての抽象メソッドを定義しない限り、インスタンス化できません。しかし、多くの場合、Decorator パターンを実装するときは、いくつかの抽象メソッドだけを定義し、その他は装飾されたオブジェクトに委譲するだけです。これを行う方法?

たとえば、次のコードを機能させたいと考えています。

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 クラスを拡張するクラスの自動委譲を提供する他のいくつかのアプローチも検討される場合があります。

PSここで解決策として継承()を提案しないでくださいclass MyElement(StandardElement)...上記のコードは単なる例です。私の実際のケースでは、MyElement は StandardElement とはかなり異なります。それでも、誰かが StandardElement の代わりに MyElement を使用することになっている場合があるため、MyElement を StandardElement と互換性のあるものにする必要があります。ここでは、「is a」ではなく「has a」関係を実装する必要があります。

4

3 に答える 3

4

デフォルトでは、必要な委任を自動的に行う方法はありません。委任は実際には抽象クラスの明示的な部分ではないため、驚くべきことではありません。ただし、欠落しているメソッドを追加する独自の委任メタクラスを作成できます。

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)

name関数の作成後に変更されない名前空間が必要なため、委任メソッドは別の関数で作成されます。__new__Python 3 では、キーワードのみの引数にデフォルト値を指定することでメソッド内ですべてのことを行うことができましたdelegatorが、Python 2 にはキーワードのみの引数がないため、これはうまくいきません。

使用方法は次のとおりです。

class MyElement(IElement):
    __metaclass__ = DelegatingMeta

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

    def click(self):
        return "my click"

self._delegateメタクラスによって作成されたメソッドが使用するオブジェクトを設定に代入します。そうしたければ、それを何らかの方法にすることもできますが、これが最も簡単な方法のように思われました。

于 2016-12-24T02:46:30.057 に答える
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()

を使用して多くのメソッドを委任したい場合、 を直接実行しないと検査できない必要なメソッドを動的に検索するため、 を介してそれを行うことは不可能に思え__getattr__ます。abc.abstractmethod__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()
于 2016-12-24T02:15:11.277 に答える