pad
pad

Reputation: 2396

Getting a decorator with args to work with method: self does not exist

When I try to decorate a method with arguments in the decorate it gives me a self is not defined. if instead I change assign val as a class variable and use MyCls.val it now gives me MyCls is not defined! How do I decorate my mehtod?

def dodecorate(VAL):
    def decorate(func):
        def wrapped(*args,**kwargs):
            res = func(*args,**kwargs)
            if res == VAL:
                res = "one"
            return res
        return wrapped
    return decorate


class MyCls(object):
    def __init__(self):
        self.val = 1

    @dodecorate(VAL = self.val)
    def onefrom1(self, x):
        return x

EDIT The above was an abstraction I invented to represent the original. Here is the actual code.

def retry_on_invalid_schema(MAX_RETRIES):
    def retrier(func):
        def wrapped(*args, **kwargs):
            tries = 0
            res = None
            while tries < MAX_TRIES:
                try:
                    res = func(*args, **kwargs)
                    assert res.has_key('by')
                    assert res.has_key('id')
                    break
                except AssertionError:
                    res = None
                    time.sleep(2**tries)
                    tries += 1
                    continue
            return res
        return wrapped
    return retrier



class Base(object):

    def __init__(self):

        self.MAX_RETRIES = 3

    @retry_on_invalid_schema(MAX_RETRIES = self.MAX_RETRIES)
    def _get_api_response(self, uri):
        return json.loads(self._get(uri))

    def _get(self, uri):
        return requests.get(uri).text

Upvotes: 0

Views: 91

Answers (2)

jonrsharpe
jonrsharpe

Reputation: 122091

If you want to use an existing decorator function on an instance method, note that you can redefine instance methods in __init__, calling the decorator directly rather than using the @ syntax:

class MyCls(object):

    def __init__(self):
        self.val = 1
        self.onefrom1 = dodecorate(self.val)(self.onefrom1)

    def onefrom1(self, x):
        return x

In use:

>>> a = MyCls()
>>> for x in range(3):
    print a.onefrom1(x)


0
one
2

Upvotes: 1

Ngenator
Ngenator

Reputation: 11269

Instead of trying to pass val to the decorator, you can just access it via the instance. self contains the current instance and is passed to the wrapped method in the decorator, so you can access that in the decorator as the first argument.

def decorate(func):
    def wrapped(instance, *args, **kwargs):
        res = func(instance, *args, **kwargs)
        if res == instance.val:
            res = "one"
        return res
    return wrapped


class MyCls(object):
    def __init__(self):
        self.val = 1

    @decorate
    def onefrom1(self, x):
        return x

c = MyCls()
print c.onefrom1(1)
print c.onefrom1(2)

output

one
2

Upvotes: 0

Related Questions