Python では、メソッドをオーバーライドしていることをどのようにして示すことができますか?
-
19-09-2019 - |
質問
たとえば、Java では、 @Override
アノテーションはコンパイル時にオーバーライドをチェックするだけでなく、優れた自己文書化コードを作成します。
私はドキュメントを探しているだけです (ただし、それが pylint のようなチェッカーへの指標であれば、それはおまけです)。どこかにコメントまたはドキュメント文字列を追加できますが、Python でオーバーライドを示す慣用的な方法は何ですか?
解決
更新 (2015 年 5 月 23 日):これと 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
あなたが実際にオーバーライドをチェックされるようにオーバーライド(F)を作成しない限り、これは、本当に何もなく、見た目ではありません。
しかし、それはJavaのだったように、その後、これは、Pythonで、なぜそれを書くのか?
Pythonのは、Javaではありません。コンパイル時のチェックのようなものは本当にもちろんあります。
私はドキュメンテーション文字列内のコメントがたくさんあると思います。これはあなたの方法のいずれかのユーザーがhelp(obj.method)
を入力し、メソッドがオーバーライドであることを確認することができます。
あなたはまた、明示的にユーザーがあなたの方法を提供することを意図している機能についてのアイデアを得るためにclass Foo(Interface)
を入力できるようになりますhelp(Interface.method)
とのインタフェースを拡張することができます。
他の人のようには、あなたが以下のような何かを得るように、私はgetattrib()グローバルメソッドを使用しての代わりに、内部辞書を使用することをお勧めししかし、あなた自身の使用してデコレータを作成することができますが、上記@Overideタグが存在しないJavaのとは異なり言っています
def Override(superClass):
def method(func)
getattr(superClass,method.__name__)
return method
あなたがしたい場合(GETATTRキャッチできた)独自のトライキャッチで独自のエラーを発生させるが、私はGETATTR方法が、この場合には、より良いと思います。
また、これはクラスメソッドとvairablesを含むクラスにバインドされたすべてのアイテムをキャッチする
@mkorpela で即興演奏 素晴らしい答え, 、これはバージョンです
より正確なチェック、命名、および発生したエラー オブジェクト
def overrides(interface_class):
"""
Function override annotation.
Corollary to @abc.abstractmethod where the override is not of an
abstractmethod.
Modified from answer https://stackoverflow.com/a/8313042/471376
"""
def confirm_override(method):
if method.__name__ not in dir(interface_class):
raise NotImplementedError('function "%s" is an @override but that'
' function is not implemented in base'
' class %s'
% (method.__name__,
interface_class)
)
def func():
pass
attr = getattr(interface_class, method.__name__)
if type(attr) is not type(func):
raise NotImplementedError('function "%s" is an @override'
' but that is implemented as type %s'
' in base class %s, expected implemented'
' type %s'
% (method.__name__,
type(attr),
interface_class,
type(func))
)
return method
return confirm_override
実際には次のようになります。
NotImplementedError
"基本クラスには実装されていません"
class A(object):
# ERROR: `a` is not a implemented!
pass
class B(A):
@overrides(A)
def a(self):
pass
より説明的な結果になります NotImplementedError
エラー
function "a" is an @override but that function is not implemented in base class <class '__main__.A'>
フルスタック
Traceback (most recent call last):
…
File "C:/Users/user1/project.py", line 135, in <module>
class B(A):
File "C:/Users/user1/project.py", line 136, in B
@overrides(A)
File "C:/Users/user1/project.py", line 110, in confirm_override
interface_class)
NotImplementedError: function "a" is an @override but that function is not implemented in base class <class '__main__.A'>
NotImplementedError
"期待される実装タイプ"
class A(object):
# ERROR: `a` is not a function!
a = ''
class B(A):
@overrides(A)
def a(self):
pass
より説明的な結果になります NotImplementedError
エラー
function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>
フルスタック
Traceback (most recent call last):
…
File "C:/Users/user1/project.py", line 135, in <module>
class B(A):
File "C:/Users/user1/project.py", line 136, in B
@overrides(A)
File "C:/Users/user1/project.py", line 125, in confirm_override
type(func))
NotImplementedError: function "a" is an @override but that is implemented as type <class 'str'> in base class <class '__main__.A'>, expected implemented type <class 'function'>
@mkorpelaの回答の素晴らしい点は、初期化フェーズ中にチェックが行われることです。チェックを「実行」する必要はありません。先の例を参照すると、 class B
初期化されることはありません (B()
)まだ NotImplementedError
まだ上げます。これはつまり overrides
エラーはより早く発見されます。
@mkorpelaの素晴らしい回答に基づいて、同様のパッケージを作成しました(約束します ピピ ギットハブ)さらに多くのチェックを行います。
A が B と C から継承するとします。そしてBはCから継承します。ipromise はそれをチェックします
A.f が B.f をオーバーライドする場合は、B.f が存在し、A が B から継承する必要があります。(これはオーバーライド パッケージからのチェックです)。
A.f が B.f をオーバーライドすることを宣言し、B.f が C.f をオーバーライドすることを宣言するパターンがありません。B がこのメソッドのオーバーライドを停止することを決定する可能性があるため、A.f は C.f からオーバーライドすることを宣言する必要があり、ダウンストリーム更新が発生することはありません。
A.f が C.f をオーバーライドすることを宣言するパターンはありませんが、B.f はそのオーバーライドを宣言しません。
A.f が C.f をオーバーライドすることを宣言するパターンはありませんが、B.f は一部の D.f からオーバーライドすることを宣言します。
また、抽象メソッドの実装をマークおよびチェックするためのさまざまな機能も備えています。
聞くには、最も単純で、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()