Подтвердить что ты не робот

Как построить декоратор с дополнительными параметрами?

Я хотел бы сделать декоратор, который можно использовать с параметром или без него: Что-то вроде этого:

class d(object):
    def __init__(self,msg='my default message'):
        self.msg = msg
    def __call__(self,fn):
        def newfn():
            print self.msg
            return fn()
        return newfn

@d('This is working')
def hello():
    print 'hello world !'

@d
def too_bad():
    print 'does not work'

В моем коде работает только использование декоратора с параметром: как перейти к работе (с параметром и без него)?

4b9b3361

Ответ 1

Я нашел пример, вы можете использовать @trace или @trace('msg1','msg2'): nice!

def trace(*args):
    def _trace(func):
        def wrapper(*args, **kwargs):
            print enter_string
            func(*args, **kwargs)
            print exit_string
        return wrapper
    if len(args) == 1 and callable(args[0]):
        # No arguments, this is the decorator
        # Set default values for the arguments
        enter_string = 'entering'
        exit_string = 'exiting'
        return _trace(args[0])
    else:
        # This is just returning the decorator
        enter_string, exit_string = args
        return _trace

Ответ 2

Если вы хотите взять параметры в свой декоратор, вам всегда нужно называть его как функцию:

@d()
def func():
    pass

В противном случае вам нужно попытаться определить разницу в параметрах - другими словами, вам нужно магически угадать, что означает вызывающий. Не создавайте API, который нужно угадать; последовательно говорите, о чем вы хотите начать.

Другими словами, функция должна быть либо декоратором, либо декоратором factory; это не должно быть оба.

Обратите внимание, что если все, что вы хотите сделать, это сохранить значение, вам не нужно писать класс.

def d(msg='my default message'):
    def decorator(func):
        def newfn():
            print msg
            return func()
        return newfn
    return decorator

@d('This is working')
def hello():
    print 'hello world !'

@d()
def hello2():
    print 'also hello world'

Ответ 3

Если вы не против полагаться на использование именованных аргументов, я сделал нечто похожее на то, что вам нужно:

def cached_property(method=None, get_attribute=lambda a: '_%s_cached' % (a,)):
    """
    Caches an object attribute.

    Can be used in the following forms:
    @cached_property
    @cached_property()
    @cached_property(get_attribute=lambda x: 'bla')

    @param method: the method to memoizes
    @param get_attribute: a callable that should return the cached attribute
    @return a cached method
    """
    def decorator(method):
        def wrap(self):
            private_attribute = get_attribute(method.__name__)
            try:
                return getattr(self, private_attribute)
            except AttributeError:
                setattr(self, private_attribute, method(self))
                return getattr(self, private_attribute)
        return property(wrap)
    if method:
        # This was an actual decorator call, ex: @cached_property
        return decorator(method)
    else:
        # This is a factory call, ex: @cached_property()
        return decorator

Это работает, потому что только один аргумент без ключевого слова, декорированная функция передается декоратору.

Обратите внимание, что я также использовал аргументы, переданные декорированной функции, в этом случае "я".

Ответ 4

Вы должны определить, является ли аргумент декоратору функцией, и использовать в этом случае простой декоратор. И тогда вам нужно надеяться, что вам никогда не нужно передавать только функцию параметризованному декоратору.

Ответ 5

Это сработает.

def d(arg):
    if callable(arg):
        def newfn():
            print 'my default message'
            return arg()
        return newfn
    else:
        def d2(fn):
            def newfn():
                print arg
                return fn()
            return newfn
        return d2

@d('This is working')
def hello():
    print 'hello world !'

@d
def hello2():
    print 'hello2 world !'

@d('Applying it twice')
@d('Would also work')
def hello3():
    print 'hello3 world !'

hello()
hello2()
hello3()

# output
#
# This is working
# hello world !
# my default message
# hello2 world !
# Applying it twice
# Would also work
# hello3 world !

Если функция декоратора @invocation не передается никакими явными аргументами, она вызывается с помощью функции, определенной в следующем def. Если передаются аргументы, то сначала они вызываются с ними, а затем вызывается результат , который предварительный вызов (который сам должен быть вызываемым) вызывается с определяемой функцией. В любом случае возвращаемое значение последнего или единственного вызова привязано к определенному имени функции.