I've started reading about descriptors in Python and created small test to see whether the order of attribute access is same as it is described. As I understand when attribute of object is accessed it is looked in following order:
type(instance).__dict__
- if data descriptor of given name is present, its __get__
method call is returnedinstance.__dict__
type(instance).__dict__
then __get__
method call is returned__getattr__
is called.Now, if method is non-data descriptor of type instance of given class, than by giving this descriptor __set__
function/method it would make it data descriptor, hence overwriting such function could be for example blocked or manipulated. Unfortunately that does not work and my question is simply: why is that a case? Does Python "marks" data and non-data descriptors at class initialization or I've done something wrong?
class A:
def d(self):
pass
def __set__(self, instance, value):
print("Nothing to see here...")
A.__dict__['d'].__set__ = __set__
a = A()
a.d = "value"
print(a.d) # prints "value" instead of <bound method ...>
A.__dict__['d'].__set__ = __set__
, which is just a complicated way of writing A.d.__set__ = __set__
in this case, is setting a magic method on the instance.
This is not working in Python, magic methods are only looked up on the type. And you can't set it on the type, because the type is just the builtin function type here.
However, it is possible to change a non-data descriptor into a data descriptor dynamically:
>>> class MyDescriptor:
... def __get__(self, instance, owner=None):
... return 'foo'
...
>>> class A:
... f = MyDescriptor()
...
>>> a = A()
>>> a.f
'foo'
>>> a.f = 'foo2'
>>> a.f
'foo2'
>>> MyDescriptor.__set__ = lambda self, instance, value: None
>>> a.f # now the descriptor takes precedence again over a.__dict__
'foo'