Аккуратный способ получить объект дескриптора
В питоне 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)
Есть ли лучший способ сделать это? Мне не нравится это self.__class__.__dict__
материал
2 ответа
A.attr
вызывает Python для вызова SomeDescriptor().__get__(None, A)
так что если у вас есть SomeDescriptor.__get__
вернуть self
когда inst
является None
, затем A.attr
вернет дескриптор:
class SomeDescriptor():
def __get__(self, inst, instcls):
if inst is None:
# instance attribute accessed on class, return self
return self
Затем вы получаете доступ к дескриптору с
desc = type(self).attr
Если имя атрибута известно только как строка, attr_name
тогда вы бы использовали
desc = getattr(type(self), attr_name)
Это работает, даже если self
является экземпляром подкласса A
, в то время как
desc = self.__class__.__dict__[attr_name]
будет работать только если self
это пример 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)
Это показывает A.attr
возвращает дескриптор, и a.somewhere()
работает как положено:
a = A()
print(A.attr)
# <__main__.SomeDescriptor object at 0xb7395fcc>
print(a.attr)
# 4
print(a.somewhere())
# True
Это показывает, что это работает для подклассов A
тоже. Если вы раскомментируетеdesc = self.__class__.__dict__[attr_name]
, вот увидишь b.somewhere()
вызывает ключевую ошибку:
class B(A): pass
b = B()
print(B.attr)
# <__main__.SomeDescriptor object at 0xb7395fcc>
print(b.attr)
# 4
print(b.somewhere())
# True
Кстати, даже если у вас нет полного контроля над определением SomeDescriptor, вы все равно можете обернуть его в дескриптор, который возвращает self
когда inst
Нет
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)
Таким образом, нет необходимости использовать
desc = self.__class__.__dict__[attr_name]
или же
desc = vars(type(self))['attr']
которая страдает от той же проблемы.
Да, чтобы получить доступ к дескрипторам в классе, единственный способ предотвратить descriptor.__get__
быть призванным пройти через класс __dict__
,
Вы могли бы использовать type(self)
чтобы получить доступ к текущему классу, и vars()
чтобы получить доступ к __dict__
более API-совместимыми способами:
desc = vars(type(self))['attr']
Если ваш дескриптор полностью пользовательский, вы всегда можете вернуть self
от SomeDescriptor.__get__()
если аргумент экземпляра None
, что происходит, когда вы обращаетесь к дескриптору непосредственно в классе. Вы можете бросить vars()
позвоните и идите прямо к:
desc = type(self).attr
property()
объект дескриптор делает именно это.