On Sep 30, 2019, at 02:38, Paul Moore <p.f.mo...@gmail.com> wrote:
> 
> It's (in my view) sad that the simple hasattr test is no longer
> sufficient, and in particular that if you want robustness, Python has
> changed to the point where a pseudo subclass check is the "right" way
> to check for an object that has certain properties. But I guess this
> is the case, and therefore the omission of a Subscriptable ABC is
> something that may need to be addressed.

The thing is, hasattr was _never_ the right way for some protocols, because the 
standard idiom to prevent inheriting a handful of special methods like __hash__ 
has always been to override it with __hash__ = None, and there have always been 
a few classes that did this for good reasons. I’m pretty sure it’s been like 
that since new-style classes were added, types and classes were unified, and 
all protocols were given dunder methods.

And how else would you do this? You could add custom syntax (as C++ did with `= 
delete`), but you’d need some way to record somewhere that this method should 
not be looked up in the mro.

> Maybe rather than proliferating ABCs like this, exposing the internal
> function in the ABC that does the "robust" version of the hasattr test
> would be more flexible?

I’m pretty sure I suggested this around 3.4, and it was rejected, but I can’t 
remember why.

At the time there _was_ no internal function, and the ABCs weren’t all 
consistent about handling None. Now the function exists, and is used 
consistently, but it’s private. (Actually, I think what exists is a function 
that checks N methods at once, while a public function you’d probably want to 
fit the API of hasattr… but that would be trivial.)

But meanwhile, as far as I know, nobody’s asked for that function in the years 
since 3.5—and, AFAIK, this is only the second time (after Reversible) that 
someone has asked for a new implicit ABC that wasn’t related to a brand-new 
protocol (like all the async stuff). So, I’m not sure we need to worry about 
proliferating too many of these too quickly. In theory there are dozens of 
dunder methods and vastly more possible combinations of them that you could 
come up with names for and ask for, but in practice that hasn’t happened, so 
why worry?

> I guess that depends on whether you find the
> "subclass of an ABC" approach acceptable. To me, it feels too much
> like "object oriented everywhere" languages like Java, which was
> always an issue I had with ABCs, but as Steven said, that ship has
> probably sailed by now[1].

But Python has always (or at least since 2.2) been even more objects-everywhere 
under the hood than Java. Python has types for classes, functions, bound 
methods, all kinds of things that aren’t even accessible at runtime except 
through reflection APIs in Java. The difference is that Python allows 
duck-typing the interfaces of those types. You can stick anything that meets 
the descriptor protocol by returning something that meets the callable protocol 
in place of a method, and it works.

Python never had a good way to check for its protocols until implicit ABCs. And 
notice that implicit protocol ABCs like Iterable are not simulating 
inheritance-based subtyping like Java’s interfaces, they’re simulating 
structure subtyping like Go’s protocols. (And even the explicit ABCs are often 
there to simulate something you just can’t spell implicitly, like the 
distinction between Sequence and Mapping even though they use the same dunder 
method.)

I think the main discomfort is that Python allows both kinds of ABCs, spells 
them the same way, and gives you a bunch of both kinds in the stdlib, so it 
_feels_ like you’re doing Java-style interfaces even though you usually aren’t. 
Spelling them both the same way is weird, and I hated the idea when I first saw 
it, but in practice it turns out to work really well. (Having half of those 
ABCs also be mixins to add useful methods is a lot more weird and wrong 
theoretically—and even more useful practically.)

Also, what we’re checking for really is subtyping. And if we’d done that by 
having collections, numbers, etc. sprout a whole bunch of new isspam functions 
instead of types to check with isinstance, imagine what a nightmare extending 
that to annotations would have been. We would have had to come up with some 
kind of syntax to let you write an arbitrary predicate or something as an 
annotation, so you could write something like `def f(things: isiterable(things) 
and all(isnumber(thing) for thing in things)` instead of `def d(things: 
Iterable[Number])`. (Of course if you hate static typing, that might have made 
you happy, because coming up with a way to statically check those annotations 
would have been a decades-long project like the one C++ is still fighting over 
for bounding its generics…)
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/3YGQM466WUXBXSXRKVOYQ4TCMPTCXR6T/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to