たとえば、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!!!!!!!
overrides
オーバーライドするメソッドに独自のメソッドがない場合、オーバーライドされたメソッドのdocstringをコピーできます。
以下は、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のように書くのですか?
override
デコレーターに追加できます。
PythonはJavaではありません。もちろん、実際のコンパイル時チェックなどはありません。
docstringのコメントはたくさんあると思います。これにより、メソッドのすべてのユーザーが入力help(obj.method)
して、メソッドがオーバーライドであることを確認できます。
を使用してインターフェイスを明示的に拡張することもできますclass Foo(Interface)
。これにより、ユーザーが入力help(Interface.method)
して、メソッドが提供する予定の機能についてのアイデアを得ることができます。
@Override
Java の本当のポイントは文書化することではありません-メソッドをオーバーライドするつもりだったときに間違いを見つけることですが、新しい名前を定義することになりました(たとえば、名前のスペルを間違えたためです。Javaでは、間違った署名ですが、これはPythonの問題ではありませんが、スペルの間違いはまだです)。
@Override
コンパイル時のチェックに加えてドキュメントです。
@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
エラーがより早く検出されることを意味します。
overrides.py
。例外の種類をからTypeError
に変更する以外に何を大幅に改善できるかわかりませんNotImplementedError
。
types.MethodType
。それはあなたの答えで良い考えでした。
他の人が言ったように、Javaとは異なり@Overideタグはありませんが、上記ではデコレータを使用して独自のタグを作成できますが、内部dictを使用する代わりにgetattrib()グローバルメソッドを使用して、次のようなものを取得することをお勧めします。
def Override(superClass):
def method(func)
getattr(superClass,method.__name__)
return method
自分でgetattr()をキャッチしたい場合は、catchで独自のエラーを発生させますが、この場合は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は最も単純で、Jythonの下でJavaクラスを使用して動作します。
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()
スーパークラスを指定せずに、オーバーライド属性の名前がその属性が含まれているクラスのスーパークラスであるかどうかを確認しただけでなく、このデコレータは、オーバーライド属性がオーバーライドされたものと同じタイプでなければならないことを確認しました属性。クラスメソッドはメソッドのように扱われ、静的メソッドは関数のように扱われます。このデコレータは、呼び出し可能オブジェクト、クラスメソッド、静的メソッド、およびプロパティに対して機能します。
ソースコードについては: https //github.com/fireuser909/overrideをください。
このデコレーターは、override.OverridesMetaのインスタンスであるクラスに対してのみ機能しますが、クラスがカスタムメタクラスのインスタンスである場合は、create_custom_overrides_meta関数を使用して、オーバーライドデコレーターと互換性のあるメタクラスを作成します。テストの場合は、override .__ init__モジュールを実行します。
Python 2.6+およびPython 3.2+では、それを行うことができます(実際にシミュレートします。Pythonは関数のオーバーロードをサポートしておらず、子クラスは自動的に親のメソッドをオーバーライドします)。これにはデコレーターを使用できます。ただし、最初に、Python @decorators
とJava @Annotations
はまったく異なるものであることに注意してください。前のものは具象コードを含むラッパーであり、後のものはコンパイラーへのフラグです。
このために、最初に pip install multipledispatch
from multipledispatch import dispatch as Override
# using alias 'Override' just to give you some feel :)
class A:
def foo(self):
print('foo in A')
# More methods here
class B(A):
@Override()
def foo(self):
print('foo in B')
@Override(int)
def foo(self,a):
print('foo in B; arg =',a)
@Override(str,float)
def foo(self,a,b):
print('foo in B; arg =',(a,b))
a=A()
b=B()
a.foo()
b.foo()
b.foo(4)
b.foo('Wheee',3.14)
出力:
foo in A
foo in B
foo in B; arg = 4
foo in B; arg = ('Wheee', 3.14)
ここでは括弧を付けたデコレータを使用する必要があることに注意してください
覚えておくべきことの1つは、Pythonには関数のオーバーロードがないため、クラスBがクラスAから継承せず、それらすべてfoo
を必要とする場合でも、@ Overrideを使用する必要があります(ただし、エイリアス 'Overload'を使用するとその場合はより良い)