9

Python 標準ライブラリには、引数を受け入れるデコレータを作成するためのショートカットがありますか?

たとえば、次のようなデコレータを書きたい場合with_timeout(timeout):

@with_timeout(10.0)
def cook_eggs(eggs):
    while not eggs.are_done():
        eggs.cook()

私は次のようなものを書く必要があります:

def with_timeout(timeout):
    _func = [None]
    def with_timeout_helper(*args, **kwargs):
        with Timeout(timeout):
            return _func[0](*args, **kwargs)
    def with_timeout_return(f):
        return functools.wraps(f)(with_timeout_helper)
    return with_timeout_return

しかし、それは非常に冗長です。引数を受け入れるデコレータを書きやすくするショートカットはありますか?

: ネストされた 3 つの関数を使用して、引数付きのデコレータを実装することも可能であることは認識していますが、それも少し最適ではないように感じます。

たとえば、@decorator_with_arguments関数のようなものかもしれません:

@decorator_with_arguments
def timeout(f, timeout):
    @functools.wraps(f)
    def timeout_helper(*args, **kwargs):
        with Timeout(timeout):
            return f(*args, **kwargs)
    return timeout_helper
4

5 に答える 5

7

正直に言うと、私はデコレータをクラスとして書く傾向があります

class TestWithArgs(object):
    def __init__(self, *deco_args, **deco_kwargs):
        self.deco_args = deco_args
        self.deco_kwargs = deco_kwargs
    def __call__(self, func):
        def _wrap(self, *args, **kwargs):
            print "Blah blah blah"
            return func(*args, **kwargs)
        return _wrap

わずかに明確ではないにしても、それは何もありません

于 2012-05-16T01:17:20.130 に答える
4

最適ではないとおっしゃっていたのは承知していますが、3 つのネストされたモデルを使用することが最もクリーンなソリューションであると感じています。内側の 2 つの関数は、引数を取る関数のデコレータを定義する「通常の」方法です ( @wraps の python のドキュメントの例を参照してください)。外側のものは、実際には、引数を取り、デコレータを返す単なる関数です。

def with_timeout(timeout):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            with Timeout(timeout):
                return f(*args, **kwargs)
        return wrapper
    return decorator
于 2012-05-16T02:12:33.137 に答える
4

Jakob の提案に基づいて、私は小さなDecoratorクラスを実装しました。

class Decorator(object):
    def __call__(self, f):
        self.f = f
        return functools.wraps(f)(lambda *a, **kw: self.wrap(*a, **kw))

    def wrap(self, *args, **kwrags):
        raise NotImplemented("Subclasses of Decorator must implement 'wrap'")

class with_timeout(Decorator):
    def __init__(self, timeout):
        self.timeout = timeout

    def wrap(self, *args, **kwargs):
        with Timeout(timeout):
            return self.f(*args, **kwargs)
于 2012-05-16T03:34:44.520 に答える
0

まず、小さなメタデコレータを定義できます。

def decorator_with_arguments(wrapper):
    return lambda *args, **kwargs: lambda func: wrapper(func, *args, **kwargs)

これにより、次のような引数を受け入れるデコレータを作成できます。

@decorator_with_arguments
def my_wrapper(func, *decorator_args, **decorator_kwargs):
    def wrapped(*call_args, **call_kwargs):
        print "from decorator:", decorator_args, decorator_kwargs
        func(*call_args, **call_kwargs)
    return wrapped

その後、通常どおり使用できます。

@my_wrapper(1, 2, 3)
def test(*args, **kwargs):
    print "passed directly:", args, kwargs

test(4, 5, 6)

装飾の追加functools.wrapsは演習として残します:)

于 2012-05-16T03:43:06.540 に答える
0

ラムダを使用しない別の方法:

def decorator_with_arguments(f):
    @functools.wraps(f)
    def with_arguments_helper(*args, **kwargs):
        def decorator(g):
            return f(g, *args, **kwargs)
        return decorator
    return with_arguments_helper
于 2012-05-20T15:51:45.410 に答える