15

私はデコレータを書いていますが、さまざまな厄介な理由[0]で、ラップしている関数がスタンドアロンで定義されているのか、クラスの一部として定義されているのか(さらに、新しいクラスがサブクラス化されているクラス)を確認すると便利です。 。

例えば:

def my_decorator(f):
    defined_in_class = ??
    print "%r: %s" %(f, defined_in_class)

@my_decorator
def foo(): pass

class Bar(object):
    @my_decorator
    def bar(self): pass

印刷する必要があります:

<function foo …&gt;: False
<function bar …&gt;: True

また、注意してください:

  • デコレータが適用された時点では、関数はバインドされていないメソッドではなく関数のままであるため、インスタンス/バインドされていないメソッド(typeofまたはを使用inspect)のテストは機能しません。
  • この問題を解決する提案のみを提供してください—この目的を達成するための同様の方法がたくさんあることを知っています(たとえば、クラスデコレータを使用する)が、後でではなく、装飾時にそれらを実行したいと思います。

[0]:具体的には、でパラメーター化されたテストを簡単に実行できるようにするデコレーターを作成していますnose。ただし、のサブクラスでテストジェネレータを実行しnoseないのでunittest.TestCase、デコレータがのサブクラス内で使用されているかどうかを判断TestCaseし、適切なエラーで失敗するようにしたいと思います。明らかな解決策-isinstance(self, TestCase)ラップされた関数を呼び出す前に使用することは機能しません。ラップされた関数はジェネレーターである必要があり、まったく実行されないためです。

4

6 に答える 6

15

inspect.stack()メソッドをラップするときの出力を見てください。デコレータの実行が進行中の場合、現在のスタックフレームはデコレータへの関数呼び出しです。次のスタックフレームは@、新しいメソッドに適用されているラッピングアクションです。3番目のフレームはクラス定義自体になります。クラス定義は独自の名前空間であるため(実行が完了するとクラスを作成するためにラップされます)、別のスタックフレームに値します。

したがって、私は提案します:

defined_in_class = (len(frames) > 2 and
                    frames[2][4][0].strip().startswith('class '))

これらのクレイジーなインデックスがすべて保守不可能に見える場合は、次のように、フレームを1つずつ分解することでより明確にすることができます。

import inspect
frames = inspect.stack()
defined_in_class = False
if len(frames) > 2:
    maybe_class_frame = frames[2]
    statement_list = maybe_class_frame[4]
    first_statment = statement_list[0]
    if first_statment.strip().startswith('class '):
        defined_in_class = True

ラッパーが実行されている時点では、クラス名または継承階層についてPythonに問い合わせる方法がないことに注意してください。クラスの作成はまだ完了していないため、その時点は処理ステップの「早すぎる」です。自分で始まる行を解析してからclass、そのフレームのグローバルを調べてスーパークラスを見つけるか、frames[1]コードオブジェクトを調べて、何を学習できるかを確認します。クラス名frames[1][0].f_code.co_nameは上記のコードに含まれているように見えますが、私はクラスの作成が完了したときにどのスーパークラスがアタッチされるかを知る方法を見つけることができません。

于 2012-01-09T19:01:14.730 に答える
2

ここでのパーティーには少し遅れていますが、これは、クラスで定義された関数でデコレータが使用されているかどうかを判断するための信頼できる手段であることが証明されています。

frames = inspect.stack()

className = None
for frame in frames[1:]:
    if frame[3] == "<module>":
        # At module level, go no further
        break
    elif '__module__' in frame[0].f_code.co_names:
        className = frame[0].f_code.co_name
        break

受け入れられた答えに対するこの方法の利点は、たとえばpy2exeで機能することです。

于 2013-08-27T17:52:16.240 に答える
1

私が持っているいくつかのハッキーな解決策:

import inspect

def my_decorator(f):
    args = inspect.getargspec(f).args
    defined_in_class = bool(args and args[0] == 'self')
    print "%r: %s" %(f, defined_in_class)

selfしかし、それは関数内の引数の存在を中継します。

于 2012-01-09T19:00:08.993 に答える
1

パッケージを使用して、次のことwraptを確認できます。-
インスタンス/クラスメソッド
-クラス
-独立した関数/静的メソッド:

次のプロジェクトページを参照してくださいwrapthttps ://pypi.org/project/wrapt/

于 2019-07-03T09:33:07.720 に答える
0

デコレータ自体がモジュールレベルで呼び出されているのか、他の何かの中にネストされているのかを確認できます。

defined_in_class = inspect.currentframe().f_back.f_code.co_name != "<module>"
于 2012-01-09T19:04:42.540 に答える
-3

モジュール内の関数inspect、特にisfunctionismethodは、あなたが望むことを実行すると思います。

>>> import inspect
>>> def foo(): pass
... 
>>> inspect.isfunction(foo)
True
>>> inspect.ismethod(foo)
False
>>> class C(object):
...     def foo(self):
...             pass
... 
>>> inspect.isfunction(C.foo)
False
>>> inspect.ismethod(C.foo)
True
>>> inspect.isfunction(C().foo)
False
>>> inspect.ismethod(C().foo)
True

次に、 Types and Membersテーブルに従って、バインドされたメソッドまたはバインドされていないメソッド内の関数にアクセスできます。

>>> C.foo.im_func
<function foo at 0x1062dfaa0>
>>> inspect.isfunction(C.foo.im_func)
True
>>> inspect.ismethod(C.foo.im_func)
False
于 2012-01-09T18:35:26.393 に答える