Pythonでの関数名取得(lamdaに渡した関数名+デコレータ関数を通常の関数呼び出しする)
普通の関数定義であれば、
def test_function(): print(1) print(test_function.__name__) # => test_function
簡単ですね。では lambdaではどうでしょう?
test_lambda = lambda: test_function() print(test_lambda.__name__) # => <lambda>
当然、取れません。inspect等でごにょごにょしても、関数の内部定義まで取れたりはしません。
苦し紛れに、次のようにはできます。
import inspect import re print(re.findall('lambda: *(.+)\(', inspect.getsource(lambda_func))[0]) # => test_lambda
ただし、REPLではエラーとなります。
経緯とか
こんな記事があって、ちょうど時間計測ツールがあったら良いなと思っていたところだったのでパッケージ化しました。
GitHub - walkingmask/wtimeit: The wrapper of timeit
ただ、Python の buil-in モジュールである timeit も利用したかったのと、関数名を自動で取得して表示する方向にしたかったので、色々やってた感じです。
最初 timeitd (timeit decorator) にしようと思ったら既にあって、wtimeit (wrapper of timeit, walkingmask's timeit) にしましたw
追加で、関数名を使い分けるのではなく、decorator or notで動作を変更するようにしたかったのですが、関数側の引数が可変長だと難しいかもなとか、decorator での呼び出しなのか普通の呼び出しなのか判断が面倒そう(一応 inspect でできる)とかで諦めました。参考にした Django のデコレータ。
と思ったけど、timeitd を見ると、デコレータの外側の関数(なんて言うんだ)を可変長にすればいけるかもしれない感じ。更新するかも。
追記: 2019/02/27
デコレータの外側の関数(なんて言うんだ)の、outer_wrapperの前に関数が引数にある場合の処理を書けば普通にいけました。
def timeit(lambda_func=None, repeat=1, unit='', ndigits=5): if lambda_func: return wtimeit(lambda_func, repeat, unit, ndigits) def outer_wrapper(func): @wraps(func) def inner_wrapper(*args, **kwargs): return wtimeit(lambda: func(*args, **kwargs), repeat, unit, ndigits, func.__name__) return inner_wrapper return outer_wrapper
こんな感じですね。これで
@wtimeit.timeit() def test_decorator(): print(1) def test_func(): print(1) result = test_decorator() result = wtimeit.timeit(lambda: test_func())
といった感じで、デコレータとしても buit-in timeit 的な使い方もできます。この書き方普通に嬉しい。