views:

56

answers:

2

First of all I don't know if this is the right approach. I want to write a decorator class that will be used with methods of other class. Before running the method I'd like to check if all required class variables are initialized. The ideal case would be something similar to this:

class Test(object):
    def __init__(self, f):
        self.f = f
        # some magic

    def __call__(self):
        self.f.magically_get_variable(required)
        # do some checks and execute method or throw an exception

class Data(object):
    def __init__(self, a, b):
        self.a = a

    @test
    def sum(self):
        required('self.a', 'self.b')
        return self.a + self.b

If this is not the way it should be done please advise me how to do it properly.

+1  A: 

I'd say decorators are a bit unfit here for the purpose of checking if a variable exists.

Think about what you're planning to do if the required variables are not supplied: raise an exception (it's in your comment).

I'd say (based on the code above):

def sum(self):
    return self.a + self.b

And let it fail if self.a or self.b is not supplied (it will raise a NameError exception, you can catch that if you like, and you could raise your own then if you want.)

ChristopheD
+1: `NameError` tells you more than any decorator. It's built in. It always works.
S.Lott
A: 

If you insist on checking before the decorated method's body starts, the following is one reasonable way to do it...:

import functools

class test(object):
    def __init__(self, requiredvars):
        self.reqs = requiredvars.split()

    def __call__(self, f):
        @functools.wraps(f)
        def wrapper(wself, *a, **k):
          missing = set()
          for v in self.reqs:
            if not hasattr(wself, v):
              missing.add(v)
          if missing:
            msg = 'missing fields: %s' % ','.join(sorted(missing))
            raise valueerror, msg
          return f(wself, *a, **k)
        return wrapper

class data(object):
    def __init__(self, a, b):
        self.a = a

    @test('a b')
    def sum(self):
        return self.a + self.b

d = data(23, 42)
d.sum()

This emits, as you apparently desire,

Traceback (most recent call last):
  File "rev.py", line 29, in <module>
    d.sum()
  File "rev.py", line 16, in wrapper
    raise ValueError, msg
ValueError: Missing fields: b
Alex Martelli