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
“Descriptors” are objects that describe some attribute of an object. They are found in the dictionary of type objects.
Python __get__ Magic Method. Python's __get__() magic method defines the dynamic return value when accessing a specific instance and class attribute. It is defined in the attribute's class and not in the class holding the attribute (= the owner class).
A descriptor is a mechanism behind properties, methods, static methods, class methods, and super() . Descriptor protocol : In other programming languages, descriptors are referred to as setter and getter, where public functions are used to Get and Set a private variable.
Descriptors are Python objects that implement a method of the descriptor protocol, which gives you the ability to create objects that have special behavior when they're accessed as attributes of other objects.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With