While using data descriptors in building classes, I came across a strange behavior of getattr function on a class.
# this is a data descriptor
class String(object):
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
# This defines a class A with 'dot' notation support for attribute 'a'
class A(object):
a = String()
obj = A()
assert getattr(A, 'a') is A.__dict__['a']
# This raises AssertionError
LHS return an empty string, while the RHS returns an instance of String
. I thought getattr
on an object was to get the value for the key inside the __dict__
. How does getattr
function work on a class object?
getattr(A, 'a')
triggers the descriptor protocol, even on classes, so String.__get__(None, A)
is called.
That returns None
because your String.__get__()
method has no explicit return
statement.
From the Descriptor Howto:
For classes, the machinery is in
type.__getattribute__()
which transformsB.x
intoB.__dict__['x'].__get__(None, B)
.
getattr(A, 'a')
is just a dynamic from of A.a
here, so A.__dict__['x'].__get__(None, A)
is executed, which is why you don't get the same thing as A.__dict__['x']
.
If you expected it to return the descriptor object itself, you'll have to do so explicitly; instance
will be set to None
in that case:
class String(object):
def __get__(self, instance, owner):
if instance is None:
return self
def __set__(self, instance, value):
pass
This is what the property
descriptor object does.
Note that the owner
argument to descriptor.__get__
is optional; if not set you are supposed to use type(instance)
instead.