dom0
dom0

Reputation: 7486

Accessing descriptor instance

When one defines a descriptor value retrieval etc. is overriden, making the instance of the descriptor effectively unaccessible.

I.e. one can't write instance_with_descriptor_attr.descriptor_attr.some_method_on_descriptor()... won't work. My question is basically how one can still access the descriptor's instance anway...

Upvotes: 0

Views: 275

Answers (3)

nadapez
nadapez

Reputation: 2707

if the __get__ method of the descriptor dont have a "return self" statement then the descriptor can only be accessed by the __dict__ attribute of the class:

class descriptor:
    def __get__(self, instance, owner=None):
        return 1

class A:
    d = descriptor()

a = A()
a.d # --> 1
A.d # --> 1
A.__dict__['d'] # --> <__main__.descriptor object at ...>

Upvotes: 0

bj0
bj0

Reputation: 8213

As noted by eryksun, Martijn's solution works for properties but not all descriptors:

class Desc(object):
    def __init__(self, val=None):
        self.val = val

    def __get__(self, obj, cls):
        return self.val

    def __set__(self, obj, val):
        self.val = val

class Test(object):
    x = Desc(5)

>>> o = Test()
>>> print o.x
5
>>> print Test.x
5

The reason it works for property descriptors can be seen in the example property descriptor implementation in the docs: http://docs.python.org/2/howto/descriptor.html#properties

the key is the __get__ function:

def __get__(self, obj, objtype=None):
    if obj is None:
        return self
    if self.fget is None:
        raise AttributeError, "unreadable attribute"
    return self.fget(obj)

If obj is None it returns self, which is the instance of the descriptor itself. obj is the instance of the class accessing the descriptor. When you access the attribute from a class instance, obj is that instance, when you access it from a class object, then obj is None.

Changing the previous descriptor to:

class Desc(object):
    def __init__(self, val=None):
        self.val = val

    def __get__(self, obj, cls):
        if obj is None:
            return self
        return self.val

    def __set__(self, obj, val):
        self.val = val

class Test(object):
    x = Desc(5)

yields (class must be re-defined if you're using a python shell)

o = Test()
>>> print o.x
5
>>> print Test.x
<__main__.Desc object at 0x23205d0>

Upvotes: 4

Martijn Pieters
Martijn Pieters

Reputation: 1121654

You need to go up to the class itself:

type(instance_with_descriptor_attr).descriptor_attr

Demonstration:

>>> class Foo():
...     @property
...     def bar(self): return 'bar'
... 
>>> foo = Foo()
>>> foo.bar
'bar'
>>> type(foo).bar
<property object at 0x109f24310>

Upvotes: 1

Related Questions