[解決済み] 装飾された関数の署名の保存
質問
非常に汎用的なことを行うデコレータを書いたとします。たとえば、すべての引数を特定の型に変換する、ロギングを実行する、メモ化を実装する、などです。
以下はその例です。
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
>>> funny_function("3", 4.0, z="5")
22
ここまではすべて順調。しかし、1つ問題があります。装飾された関数は、元の関数のドキュメントを保持しません。
>>> help(funny_function)
Help on function g in module __main__:
g(*args, **kwargs)
幸いなことに、回避策があります。
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
今回は、関数名とドキュメントが正しいです。
>>> help(funny_function)
Help on function funny_function in module __main__:
funny_function(*args, **kwargs)
Computes x*y + 2*z
しかし、まだ問題があります:関数のシグネチャが間違っているのです。args, **kwargs" という情報はほとんど役に立ちません。
どうしたらよいでしょうか。私は2つの単純だが欠陥のある回避策を考えることができます。
1 -- docstringに正しい署名を含める。
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
これは重複しているのでよくありません。自動生成されたドキュメントでは、署名はまだ正しく表示されません。関数を更新してdocstringを変更するのを忘れたり、タイプミスをしたりしがちです。[ そうそう、docstringがすでに関数本体と重複していることは承知しています。これは無視してください。funny_function は単なるランダムな例です。 ]
2 -- デコレータを使用しない、または特定のシグネチャごとに特別な目的のデコレータを使用する。
def funny_functions_decorator(f):
def g(x, y, z=3):
return f(int(x), int(y), z=int(z))
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
これは同じシグネチャを持つ関数のセットではうまくいきますが、一般的には役に立ちません。最初に言ったように、私はデコレータを完全に一般的に使用できるようにしたいのです。
私は完全に一般的で、自動的な解決策を探しています。
そこで質問ですが、作成された後に装飾された関数署名を編集する方法はあるのでしょうか?
そうでなければ、関数署名を抽出し、装飾された関数を構築する際に "*kwargs, **kwargs" の代わりにその情報を使用するデコレータを書くことができますか?どうすればその情報を抽出できるのでしょうか? どのようにデコレートされた関数を構築すればよいのでしょうか?
他のアプローチは?
どのように解決するのですか?
-
インストール デコレーター モジュールをインストールします。
$ pip install decorator
-
の定義を適応する。
args_as_ints()
:import decorator @decorator.decorator def args_as_ints(f, *args, **kwargs): args = [int(x) for x in args] kwargs = dict((k, int(v)) for k, v in kwargs.items()) return f(*args, **kwargs) @args_as_ints def funny_function(x, y, z=3): """Computes x*y + 2*z""" return x*y + 2*z print funny_function("3", 4.0, z="5") # 22 help(funny_function) # Help on function funny_function in module __main__: # # funny_function(x, y, z=3) # Computes x*y + 2*z
Python 3.4+
functools.wraps()
stdlibから
は、Python 3.4 以降のシグネチャを保持します。
import functools
def args_as_ints(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return func(*args, **kwargs)
return wrapper
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
print(funny_function("3", 4.0, z="5"))
# 22
help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(x, y, z=3)
# Computes x*y + 2*z
functools.wraps()
は利用可能です
少なくとも Python 2.5 以降で
がありますが、そこでは署名は保存されません。
help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(*args, **kwargs)
# Computes x*y + 2*z
お知らせ
*args, **kwargs
の代わりに
x, y, z=3
.
関連
-
[解決済み] モジュールの関数名(文字列)を使って、モジュールの関数を呼び出す。
-
[解決済み] Pythonでシングルトンを作成する
-
[解決済み] 億の相対的輸入
-
[解決済み] Pythonの@propertyデコレーターはどのように機能するのでしょうか?
-
[解決済み] リストの順番を維持したまま、重複を削除するにはどうしたらいいですか?
-
[解決済み] Pandasの'Freq'タグにはどのような値が有効ですか?
-
[解決済み] Pythonで0xを使わずにhex()を使うには?
-
[解決済み] if 節の終了方法
-
[解決済み] Pythonの文字列の前にあるbという接頭辞は何を意味するのですか?
-
[解決済み] djangoのQueryDictをPythonのDictに変更するには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] functools.wrapsの機能は何ですか?
-
[解決済み] Python関数内のパラメータ名一覧の取得 [重複]。
-
[解決済み】メソッドのパラメータ名を取得する方法は?
-
[解決済み] Pythonのマルチプロセッシングプールimap_unorderedの呼び出しの進捗を表示しますか?
-
[解決済み] Pythonでコード行間にかかる時間を測定するには?
-
[解決済み] タプルのリストを複数のリストに変換するには?
-
[解決済み] djangoフレームワークでフォームフィールドから値を取得するには?
-
[解決済み] Pythonでファイルの読み込みと上書きをする
-
[解決済み] Pythonの文字列の前にあるbという接頭辞は何を意味するのですか?
-
[解決済み] 認証プラグイン 'caching_sha2_password' はサポートされていません。