Reputation: 7486
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
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
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
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