あるメソッドの docstring を使用して、別のメソッドの docstring を自動的に上書きする

StackOverflow https://stackoverflow.com/questions/71817

  •  09-06-2019
  •  | 
  •  

質問

問題:テンプレートメソッドを含むクラスがあります execute 別のメソッドを呼び出す _execute. 。サブクラスは上書きすることになっています _execute 特定の機能を実装するため。この機能は、次のドキュメント文字列に文書化する必要があります。 _execute. 。上級ユーザーは、独自のサブクラスを作成してライブラリを拡張できます。ただし、そのようなサブクラスを扱う別のユーザーは、 execute, 、そのため、次のように使用すると、正しい docstring が表示されなくなります。 help(execute).

したがって、サブクラスで次のような docstring が含まれるように基本クラスを変更するとよいでしょう。 execute は自動的に次のものに置き換えられます _execute. 。これをどのように行うか何かアイデアはありますか?

これをユーザーに対して完全に透過的にするために、メタクラスを考えていました。

役に立ちましたか?

解決

サブクラスで元のメソッドをコピーしても構わない場合は、次のテクニックを使用できます。

import new

def copyfunc(func):
    return new.function(func.func_code, func.func_globals, func.func_name,
                        func.func_defaults, func.func_closure)

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        for key in attrs.keys():
            if key[0] == '_':
                skey = key[1:]
                for base in bases:
                    original = getattr(base, skey, None)
                    if original is not None:
                        copy = copyfunc(original)
                        copy.__doc__ = attrs[key].__doc__
                        attrs[skey] = copy
                        break
        return type.__new__(meta, name, bases, attrs)

class Class(object):
    __metaclass__ = Metaclass
    def execute(self):
        '''original doc-string'''
        return self._execute()

class Subclass(Class):
    def _execute(self):
        '''sub-class doc-string'''
        pass

他のヒント

基本クラスをオーバーライドできない理由はありますか? execute 直接機能しますか?

class Base(object):
    def execute(self):
        ...

class Derived(Base):
    def execute(self):
        """Docstring for derived class"""
        Base.execute(self)
        ...stuff specific to Derived...

上記を実行したくない場合は、次のようにします。

メソッド オブジェクトは、への書き込みをサポートしていません。 __doc__ 属性なので変更する必要があります __doc__ 実際の関数オブジェクト内で。基本クラスのクラスをオーバーライドしたくないため、各サブクラスに独自のコピーを与える必要があります。 execute:

class Derived(Base):
    def execute(self):
        return Base.execute(self)

    class _execute(self):
        """Docstring for subclass"""
        ...

    execute.__doc__= _execute.__doc__

しかし、これは回りくどい再定義方法に似ています。 execute...

functools.wraps() デコレータを見てください。これはすべて実行しますが、適切なコンテキストで実行できるかどうかは直接わかりません

さて、doc-stringは次の場所に保存されます __doc__ したがって、次のドキュメント文字列に基づいてそれを再割り当てすることはそれほど難しくありません。 _execute 事後に。

基本的に:

class MyClass(object):
    def execute(self):
        '''original doc-string'''
        self._execute()

class SubClass(MyClass):
    def _execute(self):
        '''sub-class doc-string'''
        pass

    # re-assign doc-string of execute
    def execute(self,*args,**kw):
        return MyClass.execute(*args,**kw)
    execute.__doc__=_execute.__doc__

Execute は、doc 文字列が実行のバージョンに付加されるように再宣言する必要があります。 SubClass そしてのためではありません MyClass (そうしないと他のサブクラスに干渉する可能性があります)。

これはあまりきちんとした方法ではありませんが、ライブラリのユーザーの視点から見ると、望ましい結果が得られるはずです。次に、これをメタクラスでラップして、サブクラス化する人が簡単にできるようにします。

これにアプローチする最も簡単で最も Python らしい方法は、単純にサブクラスで実行を再定義し、基本クラスの実行メソッドを呼び出すことであることに私は同意します。

class Sub(Base):
    def execute(self):
        """New docstring goes here"""
        return Base.execute(self)

これは、必要なことを実現するための非常に小さなコードです。唯一の欠点は、Base を拡張するすべてのサブクラスでこのコードを繰り返す必要があることです。ただし、これは、必要な動作に対して支払うべき小さな代償です。

実行用の docstring が動的に生成されることを確認する、ずさんで冗長な方法が必要な場合は、記述子プロトコルを使用できます。これは、ここで提案されている他の提案よりも大幅にコードが少なくなります。既存の関数に記述子を設定することはできないため、これは厄介です。つまり、実行は、 __call__ 方法。

これを行うコードは次のとおりですが、上記の例はより単純で、より Python 的であることに注意してください。

class Executor(object):
    __doc__ = property(lambda self: self.inst._execute.__doc__)

    def __call__(self):
        return self.inst._execute()

class Base(object):
    execute = Executor()

class Sub(Base):
    def __init__(self):
        self.execute.inst = self

    def _execute(self):
        """Actually does something!"""
        return "Hello World!"

spam = Sub()
print spam.execute.__doc__  # prints "Actually does something!"
help(spam)                  # the execute method says "Actually does something!"
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top