Search code examples
pythonpython-descriptorspython-attrs

Why is object.__setattr__.__get__(self, Attribute) used for Attribute's __init__ in attrs/_make.py


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)?


Solution

  • 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.