According to Python's documentation,
Data descriptors with
__set__()
and__get__()
defined always override a redefinition in an instance dictionary.
I have no problem understanding this sentence, but can someone clarify for me why such a rule is in place? After all, if I want to override an attribute in an instance dictionary, I already need to do that explicitely (inst.__dict__["attr"] = val
), as a naive inst.attr = val
would call the descriptor's __set__
method, which would (usually) not override the attribute in the instance dictionary.
edit: just to make it clear, I understand what is happening, my question is about why such a rule was put in place.
In general, a descriptor is an object attribute with a binding behavior, one whose attribute access is overridden by methods in the descriptor protocol. Those methods are __get__ , __set__ , and __delete__ . If any of these methods are defined for an object, it is said to be a descriptor.
They 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 are accessed as attributes of other objects. A descriptor is a mechanism behind properties, methods, static methods, class methods and super().
__get__(self, obj, type=None) : This attribute is called when you want to retrieve the information (value = obj. attr) , and whatever it returns is what will be given to the code that requested the attribute's value. gfg.
“Descriptors” are objects that describe some attribute of an object. They are found in the dictionary of type objects.
The override applies to descriptors that are part of the class __dict__
.
Python will always look up type(instance).__dict__[attributename].__get__(instance, type(instance))
, and will not use instance.__dict__
to search for a instance-override.
Here is an example using a contrived Descriptor
class and a property (which is a descriptor with a __get__
and a __set__
:
>>> class Descriptor(object):
... def __init__(self, name):
... self.name = name
... def __get__(self, instance, cls):
... print 'Getting %s, with instance %r, class %r' % (self.name, instance, cls)
...
>>> class Foo(object):
... _spam = 'eggs'
... @property
... def spam(self):
... return self._spam
... @spam.setter
... def spam(self, val):
... self._spam = val
...
>>> Foo().spam
'eggs'
>>> foo = Foo()
>>> foo.__dict__['spam'] = Descriptor('Override')
>>> foo.spam
'eggs'
As you can see, even though I add a spam
entry in the instance __dict__
, it is completely ignored and the Foo.spam
property is used still. Python is ignoring the instance __dict__
because the spam
property defines both __get__
and a __set__
.
If you use a descriptor that doesn't define a __set__
the override works (but it's __get__
is not called:
>>> class Foo(object):
... desc = Descriptor('Class-stored descriptor')
...
>>> Foo.desc
Getting Class-stored descriptor, with instance None, class <class '__main__.Foo'>
>>> Foo().desc
Getting Class-stored descriptor, with instance <__main__.Foo object at 0x1018df510>, class <class '__main__.Foo'>
>>> foo = Foo()
>>> foo.__dict__['desc'] = Descriptor('Instance-stored descriptor')
>>> foo.desc
<__main__.Descriptor object at 0x1018df1d0>
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