たとえば、Javaでは、@Override
アノテーションはオーバーライドのコンパイル時チェックを提供するだけでなく、優れた自己文書化コードを作成します。
私はドキュメントを探しています(ただし、それがpylintのようなチェッカーの指標である場合、それはボーナスです)。コメントやdocstringをどこかに追加できますが、Pythonでオーバーライドを示す慣用的な方法は何ですか?
たとえば、Javaでは、@Override
アノテーションはオーバーライドのコンパイル時チェックを提供するだけでなく、優れた自己文書化コードを作成します。
私はドキュメントを探しています(ただし、それがpylintのようなチェッカーの指標である場合、それはボーナスです)。コメントやdocstringをどこかに追加できますが、Pythonでオーバーライドを示す慣用的な方法は何ですか?
これと fwc:s の回答に基づいて、pip インストール可能なパッケージを作成しましたhttps://github.com/mkorpela/overrides
時々、私はこの質問を見てここに行き着きます。これは主に、コード ベースで同じバグを (再び) 見た後に発生します。「インターフェース」のメソッドの名前を変更しているときに、誰かが「インターフェース」実装クラスを忘れた..
Python は Java ではありませんが、Python には強力な機能があり、明示的なものは暗黙的なものよりも優れています。現実の世界には、これが役に立った具体的なケースがいくつかあります。
これがオーバーライド デコレータのスケッチです。これにより、パラメーターとして指定されたクラスが、装飾されているメソッドと同じメソッド (または何か) の名前を持っていることが確認されます。
より良い解決策を考えられる場合は、ここに投稿してください。
def overrides(interface_class):
def overrider(method):
assert(method.__name__ in dir(interface_class))
return method
return overrider
次のように機能します。
class MySuperInterface(object):
def my_method(self):
print 'hello world!'
class ConcreteImplementer(MySuperInterface):
@overrides(MySuperInterface)
def my_method(self):
print 'hello kitty!'
欠陥のあるバージョンを実行すると、クラスのロード中にアサーション エラーが発生します。
class ConcreteFaultyImplementer(MySuperInterface):
@overrides(MySuperInterface)
def your_method(self):
print 'bye bye!'
>> AssertionError!!!!!!!
これは、interface_class 名の指定を必要としない実装です。
import inspect
import re
def overrides(method):
# actually can't do this because a method is really just a function while inside a class def'n
#assert(inspect.ismethod(method))
stack = inspect.stack()
base_classes = re.search(r'class.+\((.+)\)\s*\:', stack[2][4][0]).group(1)
# handle multiple inheritance
base_classes = [s.strip() for s in base_classes.split(',')]
if not base_classes:
raise ValueError('overrides decorator: unable to determine base class')
# stack[0]=overrides, stack[1]=inside class def'n, stack[2]=outside class def'n
derived_class_locals = stack[2][0].f_locals
# replace each class name in base_classes with the actual class type
for i, base_class in enumerate(base_classes):
if '.' not in base_class:
base_classes[i] = derived_class_locals[base_class]
else:
components = base_class.split('.')
# obj is either a module or a class
obj = derived_class_locals[components[0]]
for c in components[1:]:
assert(inspect.ismodule(obj) or inspect.isclass(obj))
obj = getattr(obj, c)
base_classes[i] = obj
assert( any( hasattr(cls, method.__name__) for cls in base_classes ) )
return method
文書化のみを目的としてこれが必要な場合は、独自のオーバーライド デコレータを定義できます。
def override(f):
return f
class MyClass (BaseClass):
@override
def method(self):
pass
実際にオーバーライドをチェックするような方法で override(f) を作成しない限り、これは実際には見栄えに他なりません。
しかし、これは Python です。どうして Java のように書くのでしょうか。
PythonはJavaではありません。もちろん、コンパイル時のチェックのようなものは実際にはありません。
docstringのコメントはたくさんあると思います。これにより、メソッドのすべてのユーザーが入力help(obj.method)
して、メソッドがオーバーライドであることを確認できます。
を使用してインターフェースを明示的に拡張することもできますclass Foo(Interface)
。これにより、ユーザーは入力help(Interface.method)
して、メソッドが提供する機能についてのアイデアを得ることができます。
他の人が言ったように、Java とは異なり @Overide タグはありませんが、デコレータを使用して独自のタグを作成できますが、内部 dict を使用する代わりに getattrib() グローバル メソッドを使用することをお勧めします。
def Override(superClass):
def method(func)
getattr(superClass,method.__name__)
return method
必要に応じて、独自の try catch で getattr() をキャッチして独自のエラーを発生させることもできますが、この場合は getattr メソッドの方が優れていると思います。
また、これは、クラスメソッドと変数を含む、クラスにバインドされたすべてのアイテムをキャッチします
@mkorpela の素晴らしい回答に基づいて、さらに多くのチェックを行う同様のパッケージ ( ipromise pypi github ) を作成しました。
がおよびA
から継承し、 がから継承するとします。B
C
B
C
モジュールipromiseは以下をチェックします:
A.f
オーバーライドする場合はB.f
、B.f
存在し、A
から継承する必要がありますB
。(これはオーバーライド パッケージからのチェックです)。
A.f
パターンがオーバーライドすることB.f
を宣言し、それがオーバーライドすることを宣言することはありませんC.f
。このメソッドのオーバーライドを停止することを決定する可能性があるため、からA
オーバーライドすると言う必要があります。これにより、ダウンストリームの更新が発生することはありません。C.f
B
パターンがA.f
オーバーライドすることを宣言していませんが、そのオーバーライドを宣言してC.f
いB.f
ません。
A.f
オーバーライドすることを宣言するパターンはありませんが、C.f
いくつB.f
かの からオーバーライドすることを宣言しますD.f
。
また、抽象メソッドの実装をマークおよびチェックするためのさまざまな機能も備えています。
Hear は最も単純で、Java クラスを使用して Jython で動作します。
class MyClass(SomeJavaClass):
def __init__(self):
setattr(self, "name_of_method_to_override", __method_override__)
def __method_override__(self, some_args):
some_thing_to_do()