Am 24.04.2021 um 01:26 schrieb Gregory P. Smith:
Practically speaking, one issue I have is how easy it is to write
isinstance or issubclass checks. It has historically been much more
difficult to write and maintain a check that something looks like a duck.
`if hasattr(foo, 'close') and hasattr(foo, 'seek') and hasattr(foo,
'read'):`
Just does not roll off the figurative tongue and that is a relatively
simple example of what is required for a duck check.
To prevent isinstance use when a duck check would be better, we're
missing an easy builtin elevated to the isinstance() availability
level behaving as lookslikeaduck() that does matches against a (set
of) declared typing.Protocol shape(s). An implementation of this
exists -
https://www.python.org/dev/peps/pep-0544/#runtime-checkable-decorator-and-narrowing-types-by-isinstance
<https://www.python.org/dev/peps/pep-0544/#runtime-checkable-decorator-and-narrowing-types-by-isinstance>
- but it requires the protocols to declare runtime checkability and
has them work with isinstance similar to ABCs... technically accurate
/BUT via isinstance/? Doh! It promotes the use of isinstance when it
really isn't about class hierarchy at all...
Edit: Maybe that's okay, isinstance can be read leniently to mean "is
an instance of something that one of these things over here says it
matches" rather than meaning "a parent class type is..."? From a past
experience user perspective I don't read "isinstance" as "looks like a
duck" when I read code. I assume I'm not alone.
I'm using isinstance from time to time, mostly to satisfy the type
checker in some cases. I think having a "hasshape()" builtin would be a
huge win. In addition it would be useful for type checkers to better
support hasattr() for distinguishing between separate types. For
example, mypy has a problem with the following:
class A: pass
class B:
x: int
def foo(x: A | B) -> None:
if hasattr(x, "b"):
accepts_b(x)
else:
accepts_a(x)
As Nathaniel indicated, how deep do we want to go down this rabbit
hole of checking? just names? signatures and types on those? What
about exceptions (something our type system has no way to declare at
all)? and infinite side effects? At the end of the day we're
required to trust the result of whatever check we use and any
implementation may not conform to our desires no matter how much
checking we do. Unless we solve the halting problem. :P
I think a PEP to discuss these questions would be appropriate. Things
like also checking types could also be optional.
Not quite. A Protocol is merely a way to describe a structural type.
You do not /need/ to have your /implementations/ of anything inherit
from typing.Protocol. I'd /personally/ advise people /do not inherit/
from Protocol in their implementation.
+1
Luciano notes that it is preferred to define your protocols as narrow
and define them in places *where they're used*, to follow a golang
interface practice. My thinking aligns with that.
My background is with TypeScript, not go. But TypeScript uses
"interfaces" extensively to describe the shape of expected objects. I
agree mostly with you and Luciano here, but it can make sense to define
some protocols in a more visible location. Examples are the protocols in
collections.abc. In typeshed we collected a few more common protocols in
the type-check only module _typeshed.
(https://github.com/python/typeshed/tree/master/stdlib/_typeshed) These
are a bit experimental, but could eventually find their way into the
standard library.
A bit off-topic: But intersection types (as discussed here:
https://github.com/python/typing/issues/213) could also make a nice
addition to quickly compose ad-hoc protocols from pre-made protocols:
def read_stuff(f: HasRead & HasSeek) -> None: ...
That inheritance is used in the /declaration/ of the protocol is an
implementation detail because our language has never had a syntax for
declaring an interface. 544 fit within our existing language syntax.
Jukka Lehtosalo proposed a new "type" keyword over at typing-sig:
https://mail.python.org/archives/list/typing-...@python.org/thread/LV22PX454W4VDTXY6NDJV7NZD4LFK464/
This could also be used to define protocols a bit more succintly, and
with additional syntax constraints, making protocols feel a bit more
"first class" than they feel now.
- Sebastian
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at
https://mail.python.org/archives/list/python-dev@python.org/message/VJEBP3U37RFN36TJDNLDQTHKCW7D4KHJ/
Code of Conduct: http://python.org/psf/codeofconduct/