A.attr
causes Python to call SomeDescriptor().__get__(None, A)
so if you have SomeDescriptor.__get__
return self
when inst
is None
, then A.attr
will return the descriptor:
class SomeDescriptor():
def __get__(self, inst, instcls):
if inst is None:
# instance attribute accessed on class, return self
return self
Then you access the descriptor with
desc = type(self).attr
If the attribute's name is known only as a string, attr_name
, then you would use
desc = getattr(type(self), attr_name)
This works even if self
is a instance of a subclass of A
, whereas
desc = self.__class__.__dict__[attr_name]
would only work if self
is an instance of A
.
class SomeDescriptor():
def __get__(self, inst, instcls):
if inst is None:
# instance attribute accessed on class, return self
return self
return 4
class A():
attr = SomeDescriptor()
def somewhere(self):
attr_name = 'attr'
desc = getattr(type(self), attr_name)
# desc = self.__class__.__dict__[attr_name] # b.somewhere() would raise KeyError
return isinstance(desc, SomeDescriptor)
This shows A.attr
returns the descriptor, and a.somewhere()
works as expected:
a = A()
print(A.attr)
# <__main__.SomeDescriptor object at 0xb7395fcc>
print(a.attr)
# 4
print(a.somewhere())
# True
This shows it works for subclasses of A
too. If you uncomment
desc = self.__class__.__dict__[attr_name]
, you'll see
b.somewhere()
raises a KeyError:
class B(A): pass
b = B()
print(B.attr)
# <__main__.SomeDescriptor object at 0xb7395fcc>
print(b.attr)
# 4
print(b.somewhere())
# True
By the way, even if you do not have full control over the definition of SomeDescriptor, you can still wrap it in a descriptor which returns self
when inst
is None:
def wrapper(Desc):
class Wrapper(Desc):
def __get__(self, inst, instcls):
if inst is None: return self
return super().__get__(inst, instcls)
return Wrapper
class A():
attr = wrapper(SomeDescriptor)()
def somewhere(self):
desc = type(self).attr
# desc = self.__class__.__dict__[attr_name] # b.somewhere() would raise KeyError
return isinstance(desc, SomeDescriptor)
So there is no need to use
desc = self.__class__.__dict__[attr_name]
or
desc = vars(type(self))['attr']
which suffers from the same problem.
type(self).__dict__['attr']
? – Tarpan