Prevent decorator from being used twice on the same function in python

后端 未结 4 2016
既然无缘
既然无缘 2020-12-20 07:42

I have a decorator:

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


        
相关标签:
4条回答
  • 2020-12-20 08:13

    Look at f.func_code, it can tell you if f is a function or a wrapper.

    0 讨论(0)
  • 2020-12-20 08:22

    Noam, The property of func_code to use is co_name. See below, all that is changed is two lines at top of d()'s 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
    

    Also, see for Lukáš Lalinský's approach which uses a explicitly defined property attached to the function object. This may be preferable as the "wrapper" name may be used elsewhere...

    0 讨论(0)
  • 2020-12-20 08:25

    I'll also propose my solution:

    first, create another decorator:

    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)
    

    Now every decorator you decorate with this decorator, will restrict itself to decorate a func only once:

    @DecorateOnce
    def decorate(f):
        def wrapper...
    
    0 讨论(0)
  • 2020-12-20 08:27

    I'd store the information in the function itself. There is a risk of a conflict if multiple decorators decide to use the same variable, but if it's only your own code, you should be able to avoid it.

    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
    

    Another option can be this:

    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
    

    This assumes that you control all the decorators used. If there is a decorator that doesn't copy the _decorated_with attribute, you will not know what is it decorated with.

    0 讨论(0)
提交回复
热议问题