Why list.__add__([], ()) raises TypeError instead of returning NotImplemented

Since (1).__floordiv__(1.) returns NotImplemented, I don’t understand why that one raises.
Insofar as I understand the data model, direct method calls should only return NotImplemented in the case of being passed a type that’s not handled, and let the exception be raised by Python instead of raising it themselves.

For those wondering, I’m in the process of creating a frozenlist, so a tuple that equals and adds with lists, and I had some trouble with my __radd__ and another non-builtin list subclass, RevertableList.
When I radd a normal list it works, implying list.__add__ returns NotImplemented otherwise my __radd__ would not be called, but when I radd the list subclass object the original list.__add__ call inside RevertableList.__add__ raises a TypeError before my __radd__ is called.
Note that my frozenlist is not a subclass of list, virtual or otherwise, so its interactions with list and RevertableList should be the same. Also it doesn’t seem that the implementation of RevertableList is causing the problem, since as I said it’s its call to list.__add__ that fails.
And in any case a direct call of list.__add__([], ()) raises and that’s unexpected enough to me.

1 Like

The protocol for binary operators is to try the reflected call first if the type of the right-hand object is a subclass of the type of the left-hand object, or, as in your case, if the left-hand object does not implement the tp_as_number->nb_add slot function. For example:

class A:

class B:
    def __radd__(self, other):
        return 42
>>> A() + B()

The list type does not implement any tp_as_number slot function. list.__add__ is a wrapper_descriptor (slot wrapper) that wraps calling list_concat(), which is its tp_as_sequence->sq_concat slot function.

sq_concat is a slot function that’s only used by types implemented in C. It gets mapped to __add__, as does the tp_as_number->nb_add slot function. The latter slot is the what gets defined for Python classes that implement __add__ and/or __radd__. Even though sq_concat and nb_add are both mapped to the same __add__ method, they do not implement the same behavior. sq_concat does not return NotImplemented to support a reflected call on the other object. Of course, nb_add does do this.

Thanks for the explanation ! I don’t think I understood everything, but it still looks like a bug to me or at least an undocumented point of failure, like a side-effect of what’s likely intended as an optimization.
I opened an issue about it on the repo with a reproduceable example.