1

関数の引数情報get_function_arg_data(func)を取得するために、以下のコードのように記述します。func

def get_function_arg_data(func):
    import inspect
    func_data = inspect.getargspec(func)
    args_name = func_data.args #func argument list
    args_default = func_data.defaults #funcargument default data list
    return args_name, args_default


def showduration(user_function):
    ''' show time duration decorator'''
    import time
    def wrapped_f(*args, **kwargs):
        t1 = time.clock()
        result = user_function(*args, **kwargs)
        print "%s()_Time: %0.5f"%(user_function.__name__, time.clock()-t1)
        return result
    return wrapped_f


def foo(para1, para2=5, para3=7):
    for i in range(1000):
        s = para1+para2+para3
    return s

@showduration
def bar(para1, para2, para3):
    for i in range(1000):
        s=para1+para2+para3
    return s

print get_function_arg_data(foo)
bar(1,2,3)
print get_function_arg_data(bar)


>>> 
(['para1', 'para2', 'para3'], (5, 7))
bar()_Time: 0.00012
([], None)
>>> 

get_function_arg_data()は機能しますがfoobarバーはデコレータによって装飾されます@showduration。私の質問は、デコレータを貫通して基になる関数の情報 (引数リストとデフォルト値) を取得する方法です。

ヒントをありがとう。

4

2 に答える 2

1

Michele Simionato のデコレータモジュールをインストールしたと仮定すると、モジュールの関数が期待する署名に後者が適合するように、デコレータとその中で定義されshowdurationたネストされた関数にいくつかの小さな変更を加えることで、デコレータを動作させることができます。wrapped_f()decorator.decorator()

import decorator

def showduration(user_function):
    ''' show time duration decorator'''
    import time
    def wrapped_f(user_function, *args, **kwargs):
        t1 = time.clock()
        result = user_function(*args, **kwargs)
        print "%s()_Time: %0.5f"%(user_function.__name__, time.clock()-t1)
        return result
    return decorator.decorator(wrapped_f, user_function)

ただし、モジュールは、上記のような定型的なものを次のように削減できるため、本当に優れています。

import decorator

@decorator.decorator
def showduration(user_function, *args, **kwargs):
    import time
    t1 = time.clock()
    result = user_function(*args, **kwargs)
    print "%s()_Time: %0.5f"%(user_function.__name__, time.clock()-t1)
    return result

上記の変更のいずれかのセットを使用すると、サンプル コードは次のように出力されます。

(['para1', 'para2', 'para3'], (5, 7))
bar()_Time: 0.00026                  
(['para1', 'para2', 'para3'], None)  
于 2013-07-06T13:38:24.243 に答える
1

Pythonの関数装飾の概念は非常に一般的であるため、装飾された関数を「貫通」して基礎となる関数の情報を取得する一般的な方法はないと思います。実際、一般的に言えば、何もありません。元の関数がまったく呼び出されることを要求または保証します (ただし、通常はそうです)。

したがって、より実際的な質問は次のようになります。基になる関数の引数情報を検査できるようにする独自のデコレータをどのように作成できますか?

以前に提案された簡単な方法の 1 つは、Michele Simionato のデコレータモジュールを使用することです (そして、それと互換性のあるデコレータを記述します)。

堅牢性は劣りますが、これを行う非常に簡単な方法は、質問のコードに基づいて以下に示すことです。

def get_function_arg_data(func):
    import inspect
    func = getattr(func, '_original_f', func) # use saved original if decorated
    func_data = inspect.getargspec(func)
    args_name = func_data.args #func argument list
    args_default = func_data.defaults #funcargument default data list
    return args_name, args_default

def showduration(user_function):
    '''show time duration decorator'''
    import time
    def wrapped_f(*args, **kwargs):
        t1 = time.clock()
        result = user_function(*args, **kwargs)
        print "%s()_Time: %0.5f"%(user_function.__name__, time.clock()-t1)
        return result
    wrapped_f._original_f = user_function  # save original function
    return wrapped_f

def foo(para1, para2=5, para3=7):
    for i in range(1000):
        s = para1+para2+para3
    return s

@showduration
def bar(para1, para2, para3):
    for i in range(1000):
        s=para1+para2+para3
    return s

print 'get_function_arg_data(foo):', get_function_arg_data(foo)
print 'get_function_arg_data(bar):', get_function_arg_data(bar)

変更に必要なのは、元の関数をという名前の属性に保存することだけです。この属性_original_fには、デコレータによって返されるラップされた関数が追加されます。次に、get_function_arg_data()関数は単にこの属性をチェックし、渡された装飾関数ではなく、その値に基づいて情報を返します。

このアプローチは、装飾された関数だけでは機能せず特別な属性が追加された関数のみで機能しますが、Python 2 と 3 の両方と互換性があります。

次のコードによって生成される出力:

get_function_arg_data(foo): (['para1', 'para2', 'para3'], (5, 7))
get_function_arg_data(bar): (['para1', 'para2', 'para3'], None)
于 2013-07-07T17:39:38.120 に答える