On Sun, Sep 29, 2019 at 11:27:32AM +0100, Oscar Benjamin wrote:
That's the point that I would make as well. What can you do with an object that is only known to be Subscriptable?
I can subscript it. What did you expect the answer to be?
If you want to know if an object has a length, you can ask if it is Sized, without being forced to ask if it also supports a whole bunch of methods you don't care about.
If you want to know if something supports the ``in`` operator, you can ask if it is a Container, without being forced to check for unneeded methods you have no interest in. Nobody asks, "What can you do with an object that is only known to be a Container?" because the answer is obvious: you can check whether it contains things.
Likewise for awaitable, callable, hashable etc objects.
But subscripting (indexing) is a conspicuous exception. There's no ABC for testing whether something supports subscripting.
I don't want to get into an argument about "EAFP is better than LBYL", it's not 1999 any more and the Python community has moved past that holy war. We should acknowledge that sometimes it is better to test for an interface. That's why we have ABCs in the first place.
Whether I use ABC mixins, or write my own dunders, these interfaces are composable, orthogonal building blocks. I can give a class a __contains__ method without being forced to duplicate the entire list API, or even the entire Collection API. If I want to test for that __contains__ method, why should I check for the Collection API when I don't care about iteration or length?
That's a rhetorical question, because I don't have to check for unnecessary and unneeded methods that I don't care about. We have a Container ABC and I can just ask isinstance(obj, Container).
Coming back to my purposes...
(1) I have some classes which are infinite, lazily generated "sequences" of values. (I put sequences here in inverted commas because I am using the word in the regular English sense, not the collections.abc.Sequence sense.)
I don't want to falsely advertise that these classes are Sequences by giving them an unwanted __len__ method. Is that unreasonable? I just want to not give them a __len__ method at all, which Python is perfectly happy with me doing.
And I want to test for the minimal set of methods that I need, which is getitem, not a superset of methods "getitem plus len plus iter". Is that unreasonable?
(2) I have a function which does introspection on arbitrary objects. It needs to process objects that are subscriptable differently from objects that aren't subscriptable, but it doesn't care about the additional Sequence methods. In fact, it specifically needs to distinguish unsized "sequences" from sized Sequences.
For my own purposes, I can solve these problems. I can write my own Subscriptable test, but I'll probably get it wrong. Or I can copy and paste the useful bits from collections.abc, but that's an anti-pattern for various reasons that I trust I don't have to explain. For *myself*, it doesn't matter whether 3.9 gains this functionality or not.
We can mix-n-match hashing, iteration, indexing, containment etc in our classes, without being forced to add extra methods we don't need or care about (and sometimes, actively don't want) to meet a higher-order protocol like Mapping or Sequence. We can compose new classes from individual components, but we can't check for those individual components.