Spybdai
Spybdai

Reputation: 179

how to use decorator in a class

I know there is similar question, but my scenario is somehow different: refer to codes:

class MyClass(object):
    def __init__(self, log_location)
        self.logs = logging(log_location) # create log object by the log_location, this object should be used by the decorator fucntion

    def record_log(log_object): 
        """ this is the decorator function
        """
        def deco(func):
            def wrap(*args, **kwargs):
                rs = func()

                # use log object to record log
                if rs:
                    log_object.record('success')
                else:
                    log_object.record('fail')

            return wrap
        return deco

   @record_log(self.logs) 
   def test(self):
       rs = do_some_thing
       if rs:
            return True
       return False

def main():
    my_class = MyClass()
    my_class.test()   

But, there is an error like this:

@record_log(self.logs)
NameError: name 'self' is not defined

Hos should I use the instance attribute self.logs in a decorator function in such scenario like this??

Thanks very much!

Upvotes: 5

Views: 510

Answers (2)

Vadim Landa
Vadim Landa

Reputation: 2834

There are several objections about your code:

  1. deco() is redundant. You can directly return wrap from record_log().

  2. If you only plan to decorate MyClass's methods, then there is no point in passing log_object to the decorator, as self.logs will always be used. Otherwise, consider moving the decorator to module level, as already suggested by others.

  3. The decorated method's return value is currently lost.

  4. The call to the decorated function does not pass self to it.

The proper code would therefore be:

class MyClass(object):
    def __init__(self, log_location):
        self.logs = logging(log_location)

    def record_log(func):
        """ this is the decorator function
        """
        def wrap(self):
            rs = func(self)
            # use log object to record log
            if rs:
                print 1
                self.logs.record('success')
            else:
                print 2
                self.logs.record('fail')
            return rs
        return wrap

    @record_log
    def test(self):
       rs = do_some_thing
       if rs:
            return True
       return False

Upvotes: 1

tobias_k
tobias_k

Reputation: 82889

You can not pass a reference to self or any attribute of self at this point. The @record_log line is executed (the method is decorated) before the code in main is executed, i.e. before any instance of MyClass is created at all -- in fact, even before the definition of MyClass has been completed! But remember that

@record_log(self.logs) 
def test(self, n):

is actually just syntactic sugar for

test = record_log(self.logs)(test)

So one way to work around your problem would be to redefine test in your __init__, i.e.

def __init__(self, log_location)
    self.logs = logging(log_location)
    self.test = record_log(self.logs)(self.test)

Also note that your decorator is not passing any parameters to func and not returning the results. Also, it should probably be defined on module level (before the class).

def record_log(log_object): 
    def deco(func):
        def wrap(*args, **kwargs):
            rs = func(*args, **kwargs)   # pass parameters
            if rs:
                log_object.record('success')
            else:
                log_object.record('fail')
            return rs   # return result
        return wrap
    return deco

Upvotes: 2

Related Questions