问题
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
回答1:
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.
回答2:
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