[解決済み] パラメータ付きでもパラメータなしでも使えるデコレータを作るには?
質問
Pythonのデコレータを作りたいのですが、パラメータを付けて使うこともできます。
@redirect_output("somewhere.log")
def foo():
....
を使うか、使わないか(例えば、デフォルトで出力を標準エラー出力にリダイレクトする)です。
@redirect_output
def foo():
....
それは全く可能なのでしょうか?
私は出力をリダイレクトする問題に対する別の解決策を探しているわけではなく、私が実現したい構文の一例に過ぎないことに注意してください。
どのように解決するのですか?
この質問が古いことは知っていますが、コメントのいくつかは新しいもので、実行可能な解決策はすべて基本的に同じですが、それらのほとんどはあまりきれいでなく、読みやすくもありません。
thobe の回答が言うように、両方のケースを処理する唯一の方法は、両方のシナリオをチェックすることです。 最も簡単な方法は、単一の引数があり、それが callabe であるかどうかを確認することです (注: あなたのデコレーターが 1 つの引数だけを取り、それがたまたま callable オブジェクトである場合、追加のチェックが必要になるでしょう)。
def decorator(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# called as @decorator
else:
# called as @decorator(*args, **kwargs)
最初のケースでは、通常のデコレータが行うように、渡された関数を修正またはラップしたものを返します。
2つ目のケースでは、*args, **kwargs で渡された情報を何らかの形で使用する「新しい」デコレータを返します。
これはこれでいいのですが、デコレータを作るたびにそれを書き出さなければならないのは、かなり煩わしいですし、きれいなものではありません。 その代わりに、デコレータを書き直すことなく自動的に修正できるようになるといいのですが...そのためのデコレータなのです!
次のデコレータを使用すると、デコレータを引数つきでも引数なしでも使用できるようにすることができます。
def doublewrap(f):
'''
a decorator decorator, allowing the decorator to be used as:
@decorator(with, arguments, and=kwargs)
or
@decorator
'''
@wraps(f)
def new_dec(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# actual decorated function
return f(args[0])
else:
# decorator arguments
return lambda realf: f(realf, *args, **kwargs)
return new_dec
さて、@doublewrap でデコレータを装飾すると、1つの注意点を除いて、引数ありでも引数なしでも動作します。
このデコレータのチェックは、デコレータが受け取ることのできる引数について仮定しています (つまり、単一の呼び出し可能な引数は受け取れないということです)。 今、私たちはこれを任意のジェネレーターに適用できるようにしているので、心に留めておくか、矛盾が生じる場合は修正する必要があります。
以下はその使い方を示しています。
def test_doublewrap():
from util import doublewrap
from functools import wraps
@doublewrap
def mult(f, factor=2):
'''multiply a function's return value'''
@wraps(f)
def wrap(*args, **kwargs):
return factor*f(*args,**kwargs)
return wrap
# try normal
@mult
def f(x, y):
return x + y
# try args
@mult(3)
def f2(x, y):
return x*y
# try kwargs
@mult(factor=5)
def f3(x, y):
return x - y
assert f(2,3) == 10
assert f2(2,5) == 30
assert f3(8,1) == 5*7
関連
-
[解決済み] クラスの飾りつけはどうする?
-
[解決済み] 最小限の驚き」と「変更可能なデフォルトの引数
-
[解決済み] 2次元アレイにおけるピーク検出
-
[解決済み] すべての例外をキャッチする `try`/`except` ブロックはどのように書けばよいですか?
-
[解決済み】ネストされたディレクトリを安全に作成するには?
-
[解決済み] Pythonのキャッシュライブラリはありますか?
-
[解決済み] あるオブジェクトが数であるかどうかを確認する、最もパイソン的な方法は何でしょうか?
-
[解決済み] PyQtアプリケーションのスレッド化。QtスレッドとPythonスレッドのどちらを使うか?
-
[解決済み] virtualenvsはどこに作成するのですか?
-
[解決済み] Python の sorted() はどのようなアルゴリズムを使っているのですか?重複
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Pythonでstdoutをファイルにリダイレクトする?
-
[解決済み] 前月の日時オブジェクトを返す
-
[解決済み] 2つの線分が交差しているかどうかを確認するにはどうすればよいですか?
-
[解決済み] dict を txt ファイルに書き、それを読み取る?
-
[解決済み] ファブリック経由でデプロイユーザとしてvirtualenvを有効化する
-
[解決済み] Ctrl-CでPythonスクリプトを終了できない
-
[解決済み] Pandasのデータフレーム内の文字列を'date'データ型に変換するにはどうしたらいいですか?
-
[解決済み] virtualenvsはどこに作成するのですか?
-
[解決済み] 単純な文字列からtimedeltaオブジェクトを作成する方法
-
[解決済み] あるメソッドが複数の引数のうち1つの引数で呼び出されたことを保証する