I enjoy working with attr
and decided to read some of the source code. One part that caught my eye was the following (source):
# Cache this descriptor here to speed things up later.
bound_setattr = _obj_setattr.__get__(self, Attribute)
# Despite the big red warning, people *do* instantiate `Attribute`
# themselves.
bound_setattr("name", name)
bound_setattr("default", default)
bound_setattr("validator", validator)
where _obj_setattr
is defined as object.__setattr__
.
I am curious as to why this is done. I have read this stackoverflow response and found it very useful, but it does not cover the part about __get__
. I have read a bit about descriptors
but have not fully wrapped my head around them. So, my question is, why use _obj_setattr.__get__(self, A)("name", name)
, as opposed to object.__setattr__(self, "name", name)
?
Calling the __get__
method of a function returns a bound method object. Normally you do this by doing a lookup directly on an instance, like self.__setattr__
. But this code expects to be used in situations where the __setattr__
method may be overridden in a subclass, so doing a direct lookup of object.__setattr__
and binding it to self
is preferred.
While you could do object.__setattr__(self, "name", name)
and so on, each time you use it, that would be a little bit slower (since attribute lookup is not very fast and we'd need it for object.__setattr__
), and not very convenient because we always need to pass self
manually. Binding the method to self
gives us slightly better performance and it is a more convenient thing to type, so it's a win-win.