Pythonでは、メソッドをオーバーライドしていることをどのように示すのですか?


173

たとえば、Javaでは @Override注釈はオーバーライドのコンパイル時チェックを提供するだけでなく、優れた自己文書化コードを作成します。

私はドキュメントを探しているだけです(ただし、pylintのようなチェッカーへの指標である場合、それはおまけです)。コメントやdocstringをどこかに追加できますが、Pythonでオーバーライドを示す慣用的な方法は何ですか?


13
つまり、メソッドをオーバーライドしていることを示していないのですか?それを読者自身が理解するのは読者に任せますか?
Bluu 2009

2
ええ、私はそれがコンパイルされた言語に由来するエラーが発生しやすい状況のように見えることを知っていますが、あなたはそれを受け入れなければなりません。実際には私はそれが問題の多くであるとは知りませんでした(私の場合はRubyではなくPythonですが、同じ考えです)
Ed S.

了解しました。トリプティクの答えとムコルペラの答えはどちらも単純です。私はそれが好きですが、後者は明示的で暗黙的な精神であり、間違いを間違いなく防ぐことが勝利につながります。
Bluu 2013年

1
直接同じではありませんが、抽象基本クラスは、すべての抽象メソッドがサブクラスによってオーバーライドされているかどうかを確認します。もちろん、これは具体的なメソッドをオーバーライドする場合には役に立ちません。
letmaik 2014年

回答:


208

これと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!!!!!!!

18
驚くばかり。これは私が最初に試したときにスペルミスのバグをキャッチしました。称賛。
クリストファーブランズ

7
mfbutner:メソッドが実行されるたびに呼び出されるのではなく、メソッドが作成されたときのみ呼び出されます。
mkorpela 14年

3
これはドキュメント文字列にも適しています。overridesオーバーライドするメソッドに独自のメソッドがない場合、オーバーライドされたメソッドのdocstringをコピーできます。
letmaik 2014年

5
@mkorpela、へえ、あなたのこのコードはpythonのデフォルトのlibシステムにあるべきです。なぜこれをpipシステムに入れないのですか?:P

5
@mkorpela:ああ、私はこのパッケージについてpythonコア開発者に通知することをお勧めします。コアpythonシステムにオーバーライドデコレーターを追加することを検討することをお勧めします。:)

30

以下は、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

2
少し不思議ですが、一般的な使用法がはるかに簡単になります。使用例を含めることができますか?
Bluu 2013年

@classmethodや@propertyのような組み込みのデコレータとの比較としておそらく表現される、このデコレータを使用する平均および最悪の場合のコストは何ですか?
larham1 2013

4
@ larham1このデコレータは、各呼び出しではなく、クラス定義が分析されるときに1回実行されます。したがって、プログラムの実行時間と比較すると、実行コストは重要ではありません。
Abgan 14

PEP 487のおかげで、これはPython 3.6ではるかに優れています。
Neil G

より適切なエラーメッセージを取得するには、assert any(hasattr(cls、method .__ name__)for cls in base_classes)、 'Overriden method "{}" was not found in the base class。'。format(method .__ name__)
Ivan Kovtun

14

これをドキュメント化の目的でのみ使用する場合は、独自のオーバーライドデコレータを定義できます。

def override(f):
    return f


class MyClass (BaseClass):

    @override
    def method(self):
        pass

オーバーライドを実際にチェックするような方法でoverride(f)を作成しない限り、これは本当に目を見張るものです。

しかし、これはPythonです。なぜJavaのように書くのですか?


2
検査による実際の検証をoverrideデコレーターに追加できます。
エリックカプルン2011

70
しかし、これはPythonです。なぜJavaのように書くのですか?Javaのいくつかのアイデアは他の言語に拡張する価値があり、価値があるからですか?
Piotr Dobrogost 2013

9
スーパークラスのメソッドの名前を変更するときは、2つ下のサブクラスがオーバーライドしていることを知っておくと便利です。確かに、簡単に確認できますが、言語パーサーから少しの助けがあっても害はありません。
アブガン2013年

4
それは良い考えだからです。他のさまざまな言語がこの機能を備えているという事実は、賛成でも反対でもありません。
sfkleach 2017年

6

PythonはJavaではありません。もちろん、実際のコンパイル時チェックなどはありません。

docstringのコメントはたくさんあると思います。これにより、メソッドのすべてのユーザーが入力help(obj.method)して、メソッドがオーバーライドであることを確認できます。

を使用してインターフェイスを明示的に拡張することもできますclass Foo(Interface)。これにより、ユーザーが入力help(Interface.method)して、メソッドが提供する予定の機能についてのアイデアを得ることができます。


57
@OverrideJava の本当のポイントは文書化することではありません-メソッドをオーバーライドするつもりだったときに間違いを見つけることですが、新しい名前を定義することになりました(たとえば、名前のスペルを間違えたためです。Javaでは、間違った署名ですが、これはPythonの問題ではありませんが、スペルの間違いはまだです)。
Pavel Minaev 2009

2
@ Pavel Minaev:True、ただし、特にオーバーライドの自動インジケーターがないIDE /テキストエディターを使用している場合(EclipseのJDTが行番号の横にきれいに表示するなど)、ドキュメンテーションを用意しておくと便利です。
Tuukka Mustonen、2011

2
@PavelMinaev間違っています。の主なポイントの1つは、@Overrideコンパイル時のチェックに加えてドキュメントです。
siamii

6
@siamiiドキュメントへの支援は素晴らしいと思いますが、私が目にするすべての公式のJavaドキュメントでは、コンパイル時のチェックの重要性を示しているだけです。Pavelが「間違っている」というあなたの主張を具体化してください。
アンドリューメリンガー、2014年

5

@mkorpelaで即興、素晴らしい答え、ここにバージョンがあります

より正確なチェック、命名、発生したErrorオブジェクト

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エラーがより早く検出されることを意味します。


おい!面白そうですね。ipromiseプロジェクトでプルリクエストを行うことを検討していただけますか?回答を追加しました。
Neil G

@NeilG ipromiseプロジェクトをforkして少しコーディングしました。本質的にこれを内に実装したようですoverrides.py。例外の種類をからTypeErrorに変更する以外に何を大幅に改善できるかわかりませんNotImplementedError
JamesThomasMoon1979

おい!おかげで、オーバーライドされたオブジェクトが実際にtypeを持つことを確認できませんtypes.MethodType。それはあなたの答えで良い考えでした。
Neil G

2

他の人が言ったように、Javaとは異なり@Overideタグはありませんが、上記ではデコレータを使用して独自のタグを作成できますが、内部dictを使用する代わりにgetattrib()グローバルメソッドを使用して、次のようなものを取得することをお勧めします。

def Override(superClass):
    def method(func)
        getattr(superClass,method.__name__)
    return method

自分でgetattr()をキャッチしたい場合は、catchで独自のエラーを発生させますが、この場合はgetattrメソッドの方が適していると思います。

また、これは、クラスメソッドや変数を含む、クラスにバインドされたすべてのアイテムをキャッチします。


2

@mkorpelaの素晴らしい答えに基づいて、私は同様のパッケージを書きました(ipromise pypi githubさらに多くのチェックを)を作成しました。

仮定Aから継承をBしてCBからの継承Cます。

モジュールipromise以下をチェックします。

  • A.fオーバーライドする場合B.fB.fが存在し、Aから継承する必要がありB。(これはオーバーライドパッケージからのチェックです)。

  • A.fオーバーライドすることを宣言するパターンがありませんB.f。次に、オーバーライドすることを宣言します。C.fAそれが優先されますから、と言うべきでC.fあるためB、このメソッドをオーバーライド停止することを決定するかもしれない、とその下流の更新になってはなりません。

  • あなたにはパターンがありません A.fオーバーライドすることを宣言するオーバーライドを宣言してC.fB.fません。

  • A.fオーバーライドすることを宣言するパターンがありませんC.fますが、B.fそれはいくつかから上書きされますことを宣言しますD.f

また、抽象メソッドの実装をマークおよびチェックするためのさまざまな機能があります。


0

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()

0

スーパークラスを指定せずに、オーバーライド属性の名前がその属性が含まれているクラスのスーパークラスであるかどうかを確認しただけでなく、このデコレータは、オーバーライド属性がオーバーライドされたものと同じタイプでなければならないことを確認しました属性。クラスメソッドはメソッドのように扱われ、静的メソッドは関数のように扱われます。このデコレータは、呼び出し可能オブジェクト、クラスメソッド、静的メソッド、およびプロパティに対して機能します。

ソースコードについては: https //github.com/fireuser909/overrideをください。

このデコレーターは、override.OverridesMetaのインスタンスであるクラスに対してのみ機能しますが、クラスがカスタムメタクラスのインスタンスである場合は、create_custom_overrides_meta関数を使用して、オーバーライドデコレーターと互換性のあるメタクラスを作成します。テストの場合は、override .__ init__モジュールを実行します。


0

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'を使用するとその場合はより良い)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.