7

非常に密接に関連しています:Pythonデコレータで関数のargspecをプログラムで変更するにはどうすればよいですか?

デコレータ モジュールは、装飾された関数の argspec を保持するデコレータ関数を作成する手段を提供します。

デコレータとして使用されていない関数を定義した場合、別の関数の argspec をコピーする方法はありますか?

使用例:

class Blah(object):
    def foo(self, *args, **kwargs): 
        """ a docstr """
        result = bar(*args, **kwargs)
        result = result**2 # just so it's clear we're doing something extra here...
        return result

def bar(x, y, z=1, q=2):
    """ a more useful docstr, saying what x,y,z,q do """
    return x+y*z+q

fooの argspec を のように見せたいbarのですが、ソースは変更しないでください (つまり、inspect.getsource(foo)まだresultジャンクが表示されます)。これの主な目的は、sphinx ドキュメントと ipython の対話型ヘルプを取得して、適切な引数を表示することです。

他の質問への回答が言ったように、decorator パッケージはこれを行う方法を示していますが、そのコードの本質の中で迷子になりました。パッケージがソースを再コンパイルしてdecoratorいるようです。私は、より単純なアプローチ、例えば のようなものfoo.argspec = bar.argspecが可能になることを望んでいました。

4

1 に答える 1

6

デコレータは、単に別の関数で何かを行う関数です。したがって、技術的には、必要なコードをfooメソッドのすぐ下に置くことができ、技術的にfooは、デコレーターを使用せずに変更することになりますが、それはひどい混乱になります。

必要なことを行う最も簡単な方法は、2 番目の関数 (barこの場合) を引数として取るデコレータを作成して、どの署名をコピーするかを知ることです。クラスコードは次のようになります。

class Blah(object):
    @copy_argspec(bar)
    def foo(self, *args, **kwargs): 
        """ a docstr """
        result = bar(*args, **kwargs)
        result = result**2 # just so it's clear we're doing something extra here...
        return result

barクラスの後ではなく、前に定義する必要があります。

.
.
.
. . . 時間が経ちます。. . .
.
.

幸いなことに、適応できる古いデコレータを見つけました。

help(Blah.foo)装飾前はこんな感じ。

Help on method foo in module __main__:

foo(self, *args, **kwargs) unbound __main__.Blah method
    a docstr

装飾後は次のようになります。

Help on method foo in module __main__:

foo(self, x, y, z=1, q=2) unbound __main__.Blah method
    a more useful docstr, saying what x,y,z,q do

使用したデコレータは次のとおりです。

import inspect

class copy_argspec(object):
    """
    copy_argspec is a signature modifying decorator.  Specifically, it copies
    the signature from `source_func` to the wrapper, and the wrapper will call
    the original function (which should be using *args, **kwds).  The argspec,
    docstring, and default values are copied from src_func, and __module__ and
    __dict__ from tgt_func.
    """
    def __init__(self, src_func):
        self.argspec = inspect.getargspec(src_func)
        self.src_doc = src_func.__doc__
        self.src_defaults = src_func.func_defaults

    def __call__(self, tgt_func):
        tgt_argspec = inspect.getargspec(tgt_func)
        need_self = False
        if tgt_argspec[0][0] == 'self':
            need_self = True

        name = tgt_func.__name__
        argspec = self.argspec
        if argspec[0][0] == 'self':
            need_self = False
        if need_self:
            newargspec = (['self'] + argspec[0],) + argspec[1:]
        else:
            newargspec = argspec
        signature = inspect.formatargspec(
                formatvalue=lambda val: "",
                *newargspec
                )[1:-1]
        new_func = (
                'def _wrapper_(%(signature)s):\n' 
                '    return %(tgt_func)s(%(signature)s)' % 
                {'signature':signature, 'tgt_func':'tgt_func'}
                   )
        evaldict = {'tgt_func' : tgt_func}
        exec new_func in evaldict
        wrapped = evaldict['_wrapper_']
        wrapped.__name__ = name
        wrapped.__doc__ = self.src_doc
        wrapped.func_defaults = self.src_defaults
        wrapped.__module__ = tgt_func.__module__
        wrapped.__dict__ = tgt_func.__dict__
        return wrapped
于 2013-09-05T02:14:58.590 に答える