17

class ABC「抽象基本クラス」です。class Xそのサブクラスです。

のサブクラスで実行する必要のある作業がいくつかありますがABC、これは忘れたり、間違って実行したりするのが簡単です。私は次のABC.__init__()いずれかでそのような間違いを見つけるのを手伝いたいと思います:

(1)その作業を開始する、または(2)それを検証する

super().__init__()これは、が最初に呼び出されるか、最後に呼び出されるかに影響しX.__init__()ます。

説明のための簡単な例を次に示します。

のすべてのサブクラスにABC属性が必要registryであり、それがリストである必要があるとします。ABC.__init__()(1)初期化するregistryか、(2)正しく作成されたことを確認することができます。以下は、各アプローチのサンプルコードです。

アプローチ1:ABCで初期化

class ABC:
    def __init__(self):
        self.registry = []

class X:
    def __init__(self):
        super().__init__()
        # populate self.registry here
        ...

アプローチ2:ABCで検証する

class ABC:
    class InitializationFailure(Exception):
        pass
    def __init__(self):
        try:
            if not isinstance(self.registry, list):
                raise ABC.InitializationError()
        except AttributeError:
            raise ABC.InitializationError()

class X:
    def __init__(self):
        self.registry = []
        # populate self.registry here
        ...
        super().__init__()

どちらがより良いデザインですか?

4

3 に答える 3

16

確かに、アプローチ2よりもアプローチ1の方が好まれます(アプローチ2は、抽象的な機能を実行するのではなく、ベースをタグインターフェイスに委任するため)。ただし、アプローチ1自体は、サブタイプ開発者がsuper()呼び出しを適切に実装することを忘れないようにし、初期化を確実にするという目標を達成しません。

サブタイプの実装者が初期化を忘れる可能性を軽減するために、「ファクトリ」パターンを調べることをお勧めします。検討:

class AbstractClass(object):
    '''Abstract base class template, implementing factory pattern through 
       use of the __new__() initializer. Factory method supports trivial, 
       argumented, & keyword argument constructors of arbitrary length.'''

   __slots__ = ["baseProperty"]
   '''Slots define [template] abstract class attributes. No instance
       __dict__ will be present unless subclasses create it through 
       implicit attribute definition in __init__() '''

   def __new__(cls, *args, **kwargs):
       '''Factory method for base/subtype creation. Simply creates an
       (new-style class) object instance and sets a base property. '''
       instance = object.__new__(cls)

       instance.baseProperty = "Thingee"
       return instance

この基本クラスは、次のように3行のコードsan-commmentを使用するだけで、アプローチ1よりも簡単に拡張できます。

class Sub(AbstractClass):
   '''Subtype template implements AbstractClass base type and adds
      its own 'foo' attribute. Note (though poor style, that __slots__
      and __dict__ style attributes may be mixed.'''

   def __init__(self):
       '''Subtype initializer. Sets 'foo' attribute. '''
       self.foo = "bar"

スーパークラスのコンストラクターは呼び出さなかったものの、basePropertyは初期化されることに注意してください。

Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from TestFactory import *
>>> s = Sub()
>>> s.foo
'bar'
>>> s.baseProperty
'Thingee'
>>> 

コメントが示すように、基本クラスAbstractClassはスロットを使用する必要はありません。属性を新しい()初期化子に設定することで、属性を「暗黙的に」定義することもできます。例えば:

instance.otherBaseProperty = "Thingee2"

うまくいくでしょう。また、基本クラスの初期化子は、そのサブタイプでトリビアル(引数なし)の初期化子と、可変長の拡張およびキーワード引数の初期化子をサポートしていることに注意してください。最も単純な(簡単なコンストラクター)場合には構文を課さないが、メンテナンスを課すことなくより複雑な機能を可能にするため、常にこのフォームを使用することをお勧めします。

于 2011-02-27T14:09:52.903 に答える
2

あなたが提供した例では、私はあなたのアプローチ1のようにそれを行います。しかし、クラスABCは、主にXおよび特定のインターフェースを実装する他のクラスの実装ヘルパーとして見ます。このインターフェースは、属性「レジストリ」で構成されています。

少なくとも論理的には、Xと他のクラスが共有するインターフェイスと、それを実装するのに役立つ基本クラスを区別する必要があります。つまり、リスト「レジストリ」を公開するインターフェース(「ABC」など)があることを個別に定義します。次に、(Xに加えて)新しい実装クラスを非常に簡単に導入できるため、インターフェイスABCの実装者に共通の基本クラス(概念的にはミックスイン)としてインターフェイスの実装を除外することを決定できます。

編集:クラスの実装における間違いからの保護に関しては、ユニットテストを通じてこれをターゲットにします。これは、実装のすべてを説明しようとするよりも包括的だと思います:)

于 2011-02-27T13:49:45.993 に答える
1

サブクラスは、リストを使用してレジストリを実装したことを知る必要がないため、1つ目はより優れた設計です。たとえば、_is_in_registry1つの引数を取り、要素がレジストリにあるかどうかを返す関数を提供できます。その後、スーパークラスを変更してリストをセットに置き換えることができます。これは、要素はレジストリに1回しか表示できず、サブクラスを変更する必要がないためです。

また、コードが少なくなります。このような100個のフィールドがABCあり、 ...ABCのような100個のサブクラスがあるとします。X

于 2011-02-27T12:47:55.597 に答える