Запретить использование декоратора дважды в одной и той же функции в python - PullRequest
1 голос
/ 10 октября 2009

У меня есть декоратор:

from functools import wraps
def d(f):
    @wraps(f)
    def wrapper(*args,**kwargs):
        print 'Calling func'
        return f(*args,**kwargs)
    return wrapper

И я хочу запретить ему украшать одну и ту же функцию дважды, например, запретить такие вещи, как:

@d
@d
def f():
   print 2

Единственное возможное решение, о котором я мог подумать, это использовать dict для хранения функций, которые декоратор уже декорировал, и вызывать исключение, если его попросили декорировать функцию, которая существует в dict. Скажите, есть ли у вас идея получше ...

Ответы [ 4 ]

2 голосов
/ 10 октября 2009

Я также предложу свое решение:

сначала создайте другой декоратор:

class DecorateOnce(object):
    def __init__(self,f):
        self.__f=f
        self.__called={} #save all functions that have been decorated 
    def __call__(self,toDecorate):
        #get the distinct func name
        funcName=toDecorate.__module__+toDecorate.func_name
        if funcName in self.__called:
            raise Exception('function already decorated by this decorator')
        self.__called[funcName]=1
        print funcName
        return self.__f(toDecorate)

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

@DecorateOnce
def decorate(f):
    def wrapper...
2 голосов
/ 10 октября 2009

Я бы сохранил информацию в самой функции. Существует риск конфликта, если несколько декораторов решат использовать одну и ту же переменную, но если это только ваш собственный код, вы сможете избежать этого.

def d(f):
    if getattr(f, '_decorated_with_d', False):
        raise SomeException('Already decorated')
    @wraps(f)
    def wrapper(*args,**kwargs):
        print 'Calling func'
        return f(*args,**kwargs)
    wrapper._decorated_with_d = True
    return wrapper

Другой вариант может быть таким:

def d(f):
    decorated_with = getattr(f, '_decorated_with', set())
    if d in decorated_with:
        raise SomeException('Already decorated')
    @wraps(f)
    def wrapper(*args,**kwargs):
        print 'Calling func'
        return f(*args,**kwargs)
    decorated_with.add(d)
    wrapper._decorated_with = decorated_with
    return wrapper

Предполагается, что вы контролируете все используемые декораторы. Если есть декоратор, который не копирует атрибут _decorated_with, вы не будете знать, чем он украшен.

0 голосов
/ 10 октября 2009

Noam, свойство func_code использовать co_name. Смотрите ниже, все, что изменилось, это две строки в верхней части определения d ()

def d(f):
   if f.func_code.co_name == 'wrapper':
      return f    #ignore it  (or can throw exception instead...)
   @wraps(f)
   def wrapper(*args, **kwargs):
      print 'calling func'
      return f(*args, **kwargs)
   return wrapper

Также см. Подход Лукаша Лалинского, который использует явно определенное свойство, прикрепленное к объекту функции. Это может быть предпочтительнее, так как имя «обертки» может использоваться в другом месте ...

0 голосов
/ 10 октября 2009

Посмотрите на f.func_code, он может сказать вам, является ли функция f или оболочкой.

...