防止装饰器在python中的同一个函数上使用两次

时间:2009-10-10 06:35:59

标签: python decorator

我有一个装饰者:

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 个答案:

答案 0 :(得分:2)

我将信息存储在函数本身中。如果多个装饰者决定使用相同的变量,则存在冲突的风险,但如果它只是您自己的代码,那么您应该能够避免它。

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属性,你就不会知道装饰的是什么。

答案 1 :(得分:2)

我也会提出我的解决方案:

首先,创建另一个装饰器:

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)

现在你用这个装饰器装饰的每个装饰器都会限制自己只装饰一次func:

@DecorateOnce
def decorate(f):
    def wrapper...

答案 2 :(得分:0)

Noam,func_code使用的属性为co_name。如下所示,所有改变的是d()的def

顶部的两行
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

另外,请参阅LukášLalinský的方法,该方法使用附加到函数对象的显式定义的属性。这可能是优选的,因为“包装”名称可能在其他地方使用...

答案 3 :(得分:-1)

查看f.func_code,它可以告诉您f是函数还是包装器。