2017-09-07 2 views
0

特定の戻り値の型を必要とする、私の基本クラスの設計では、次のとおりです。は私が別の作成者から必要な必要な状況を、持っている

class Derived(Base): 
    def process(self): 
     # if return sth different from ExemplaryClass, should throw exception 
:他のプログラマによって作成されます

class ExemplaryClass: 
    pass 

class Base(ABC): 
    @abstractmethod 
    def process(self): 
     # should return specific type, for instance ExemplaryClass 

と派生クラスを、

私の設計に従うために、どのようにしてプログラマーにコードを強制することができますか? C++および静的型制御と他の言語で 、この問題は非常に簡単です:

class ExemplaryClass {} 

class Base{ 
public: 
    virtual ExemplaryClass proccess() =0; 
} 

class Derived :public Base { 
public: 
    // ERROR 
    void proccess() ovveride { } 
} 

しかし、Pythonのようなダイナミック型制御と言語で、この状況は私がこの問題を解決する見当がつかない、非常に複雑です。

+8

私の設計に従うために、プログラマーにコードを強制するにはどうすればいいですか? < - 銃器や脅迫で。 – timgeb

+0

ダックタイピングの原則は、そのようなハイパー明示的なタイピングを禁止します。 'process'が*' ExemplaryClass'のように動作するものを返す限り、それは十分です。しかし、Python 3.4以降の静的型チェックツールを使用して、少なくともその一部をチェックすることができます。 – deceze

+0

@timgebあなたの答えは非常に面白いですが、Python 3.5+では正確ではありません。 – jpic

答えて

2

他の人がコメントに指摘しているように、このようなタイプチェックは悪い習慣です。私は最終的に解決策を考え出した

class Base(ABC): 
    def process(self): 
     result = self._process() 
     assert isinstance(result, ExemplaryClass) 
     return result 

    @abstractmethod 
    def _process(self): 
     raise NotImplementedError 
2

:抽象メソッドと抽象メソッドを呼び出し、その戻り値を検証する別の方法 - あなたが本当にそれをしたい場合しかし、あなたは二つの方法にprocessを分割することができます。

考え方は、メインクラスに__getattribute__を実装することです。そのため、子クラスが呼び出すメソッドを変更する必要があります。

2つの単純なクラス:モジュールによって定義されるFoo基本クラスと、APIのユーザーによって定義されるBarサブクラスを考えます。 Barクラスはmethodメソッドを上書きしますが、Foo.methodと同じ動作を保つように、タイプintのオブジェクトを返すようにします。

class Foo: 
    def method(self): 
     return 1 

class Bar(Foo): 
    def method(self): 
     return "a" 

もちろん、これは問題ありません。 しかし、ここには__getattribute__メソッドのトリックがあります。

まず、要求された属性の名前が"method"であるかどうかを確認します。 それがそうなら、実際のmethodを返す代わりに、私はカスタム関数を作成し、実際にはmethodの戻り値がintであることを示しています。

def __getattribute__(self, attribute): 
    if attribute == "method": 

     def _f(*args, **kwargs): 
      result = type(self).method(self, *args, **kwargs) 
      assert isinstance(result, int) 
      return result 

     return _f 

は今、__getattribute__方法はinstance.whateverは関係なく、その相続ファミリーの任意のクラスの属性として見つけることができるかどうかwhateverinstance.__getattribute__("whatever")への呼び出しになりますように定義されます。

したがって、bar = Bar()の場合、bar.methodは、Foo.__getattribute__で定義した内部_fに置き換えられます。

さて、bar.method()AssertionErrorになりますが、私はそれを修正する場合は、次のように:

class Bar(Foo): 
    def method(self): 
     return 1 

が実行は罰金になります。 __getattribute__の定義について


あなたがself.somethingを呼び出すことはできませんので、それは__getattribute__への再帰呼び出しにつながるので、それは、少しトリッキーかもしれません。 この解決策はtype(self).methodを直接参照することであり、クラスメソッドをここではBarで定義します。この場合 、この方法は、最初のパラメータとしてinstanceをとるので、selfを渡す必要があり、従ってライン:サブクラス自体が__getattribute__を実装している場合もちろん

result = type(self).method(self, *args, **kwargs) 

、このすべては失敗します。 しかし、とにかく、Python APIは簡単に壊れることを忘れないでください。 具体的には、AssertErrorを簡単に検出できました。 さて、あなたはsys.exit(1)assertを置き換えることができますが、それは少しも保かもしれません...


私はアイデアを得ました。 これは多かれ少なかれ、アンチ・パターン、Pythonに似ています。なぜなら、ユーザーは自分が望むように自由に行うことを禁止するからです。一方、1はするために、単なるprintによってassert/raiseを置き換えることができ は彼らを罰するユーザーの代わりに、を教育します。

+0

私はこの大胆な大胆さを愛しています。しかし、誰かが実際に試してみると、私はコードを維持する必要はないと思っています。 – trentcl

+0

@trentclまあ、コードデザインに関しては、これは私がこれまでに思いついたもっとも反ピリゾンです。少なくとも、それは楽しいです! –

関連する問題