In Python 3
class A(object):
attr = SomeDescriptor()
...
def somewhere(self):
# need to check is type of self.attr is SomeDescriptor()
desc = self.__class__.__dict__[attr_name]
return isinstance(desc, SomeDescriptor)
Is there better way to do it? I don't like this self.__class__.__dict__
stuff
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.
Yes, to access descriptors on a class, the only way to prevent descriptor.__get__
from being invoked is to go through the class __dict__
.
You could use type(self)
to access the current class, and vars()
to access the __dict__
in more API-compliant ways:
desc = vars(type(self))['attr']
If your descriptor is entirely custom, you can always return self
from SomeDescriptor.__get__()
if the instance argument is None
, which happens when you access the descriptor directly on the class. You can drop the vars()
call and go straight to:
desc = type(self).attr
The property()
descriptor object does exactly this.
来源:https://stackoverflow.com/questions/21629397/neat-way-to-get-descriptor-object