Search code examples
pythonsoftware-designdescriptor

Is it possible to test if object property uses a descriptor?


I'm not convinced this is possible, but I thought I'd ask as I'm new to Python. Given an object with a property which its value is handled by a descriptor; is it possible to know that the given descriptor type was involved?

Example Descriptor:

class Column(object):
    def __init__(self, label):
        self.label = label

    def __get__(self, obj, owner):
        return obj.__dict__.get(self.label)

    def __set__(self, obj, value):
        obj.__dict__[self.label] = value

Test Object:

class Test(object):
    name = Column("column_name")

    def add(self):
       print self.name.__class__

Executing this:

my_test = Test()
my_test.name = "myname"
my_test.add()  

This gives: <type 'str'> which is the data type of the value "myname", is it possible to test for isinstance(self.name, Descriptor) - this returns false, but I want it to return true - or something similar?

Edit - Removed bug of old-style class on Test


Solution

  • Search the object's class and superclasses in method resolution order for the descriptor object:

    def find_descriptor(instance, attrname):
        '''Find the descriptor handling a given attribute, if any.
    
        If the attribute named attrname of the given instance is handled by a
        descriptor, this will return the descriptor object handling the attribute.
        Otherwise, it will return None.
        '''
        def hasspecialmethod(obj, name):
            return any(name in klass.__dict__ for klass in type(obj).__mro__)
        for klass in type(instance).__mro__:
            if attrname in klass.__dict__:
                descriptor = klass.__dict__[attrname]
                if not (hasspecialmethod(descriptor, '__get__') or
                        hasspecialmethod(descriptor, '__set__') or
                        hasspecialmethod(descriptor, '__delete__')):
                    # Attribute isn't a descriptor
                    return None
                if (attrname in instance.__dict__ and
                    not hasspecialmethod(descriptor, '__set__') and
                    not hasspecialmethod(descriptor, '__delete__')):
                    # Would be handled by the descriptor, but the descriptor isn't
                    # a data descriptor and the object has a dict entry overriding
                    # it.
                    return None
                return descriptor
        return None