TL;DR
Python 2.7.5, when using descriptors as decorators, is there any way to pass in arguments (to the __init__ method)?
OR
How can I get access to a class instance's attributes using a method decorator with arguments (as here)? -- I believe this is not possible, though, hence the focus below on descriptors...
Long version
I have a class of objects, with different "type" attributes. Based on an instance's "type", I would like a method to be available or not. I know one way is to create multiple classes, but I'm trying to not have a bunch of if / else statements when creating these objects. For example, I have two objects A and B that are almost identical, except object B I don't want to have the get_start_date() method available. So essentially, what I want is that both A and B are instances of class MyObjects, but have a "type" attribute that is different.
type(A) == type(B)
A.genus_type != B.genus_type
I would use that .genus_type attribute to differentiate which methods are allowable and which ones not...
I was thinking I could use decorators with a whitelist, like:
def valid_for(whitelist):
    def wrap(f):
        def wrapper(*args, **kwargs):
            return f(*args, **kwargs)
    return wrapper
return wrap
class A(object):
    @valid_for(['typeB'])
    def do_something_cool(self):
        print 'blah'
But the problem was I did not have access to the actual class instance in the decorator, where I could test for the instance type attribute. Based on this SO question, I thought, "I can use descriptors!".
So I then tried:
class valid_for(object):
    """ descriptor to check the type of an item, to see
    if the method is valid for that type"""
    def __init__(self, func):
        self.f = func
    def __get__(self, instance, owner):
        def wrapper(*args):
            return self.f(instance, *args)
        return wrapper
But then I couldn't figure out how to get the ['typeB'] parameter passed into the descriptor...by default, Python passes in the called method as the argument to __init__. I could create hard-coded descriptors for each type and nest them, but then I wonder if I will run into this problem. Assuming I could overcome the nesting issue, it also seems less clean to do something like:
class A(object):
    @valid_for_type_b
    @valid_for_type_f
    @valid_for_type_g
    def do_something_cool(self):
        print 'blah'
Doing something like this just made my func equal to the list ['typeB']...
class valid_for(object):
    """ descriptor to check the type of an item, to see
    if the method is valid for that type"""
    def __init__(self, func, *args):
        self.f = func
    def __get__(self, instance, owner):
        def wrapper(*args):
            return self.f(instance, *args)
        return wrapper
class A(object):
    @valid_for(['typeB'])
    def do_something_cool(self):
        print 'blah'
And my func is not in the *args list, so I can't just do a simple swap of arguments (*args is empty).
I've been looking for hints here and here, but haven't found anything that seems like a clean or valid workaround. Is there a clean way to do this, or do I have to use multiple classes and just mix-in the various methods? Or, right now I am leaning towards an instance method that checks, but that seems less clean and reusable...
class A(object):
    def _valid_for(self, whitelist):
        if self.genus_type not in whitelist:
            raise Exception
    def do_something_cool(self):
        self._valid_for(['foo'])
        print 'blah'
I am using Python 2.7.5.
UPDATE 1
Per a suggestion in the comments, I tried:
def valid_for2(whitelist):
    def wrap(f):
        def wrapper(*args, **kwargs):
            import pdb
            pdb.set_trace()
            print args[0].genus_type
            return f(*args, **kwargs)
        return wrapper
    return wrap
But at this point, args[0]. just returns the args:
(Pdb) args[0]
args = (<FormRecord object at 0x112f824d0>,)
kwargs = {}
(Pdb) args[0].genus_type
args = (<FormRecord object at 0x112f824d0>,)
kwargs = {}
However, using functools as suggested does work -- so I will award the answer. There seems to be some black magic in functools that lets the arguments in?
UPDATE 2
So investigating jonrsharpe's suggestion more, his method also seems to work, but I have to explicitly use self instead of args[0]. Not sure why the behavior is different...
def valid_for2(whitelist):
    def wrap(f):
        def wrapper(self, *args, **kwargs):
            print self.genus_type
            return f(*args, **kwargs)
        return wrapper
    return wrap
results in the same output as with functools.
Thanks!
 
     
    