-
Notifications
You must be signed in to change notification settings - Fork 241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Protocols (a.k.a. structural subtyping) #11
Comments
It's meant to be used for structural subtyping. It is quite similar to ABCs, but neither is a replacement for the other. The details of how to use Here is an example of how
There is no need to explicitly declare that file objects implement The current implementation of |
Protocols look to me like a pretty reasonable way of implementing structural typing, which is definitely something that we're hoping to see in the PEP. This design is necessarily verbose; an in-line, anonymous definition using dictionaries, like
is a bit more concise when the protocol/structural type is small. This would conflict with the current proposal to use dictionaries in annotations to represent current non-type uses of annotations (https://github.com/ambv/typehinting/blob/master/pep-NNNN.txt#L194), however see #26 for thoughts on alternatives. |
I haven't seen any evidence for protocol types being used/useful all over the place, so my current working hypothesis is that having a heavy-weight syntax for protocols only adds a trivial amount of syntactic overhead for the vast majority of programs. However, the absence of evidence doesn't prove anything -- if somebody finds a few counterexamples I'm happy to change my mind. :) Maybe we could have a look at some Go code and see how many small, throwaway structural types are used there? Of course, having convenient syntax for a feature may nudge programmers into using the feature more often. |
ABCs have runtime instance checks so it's hard for the type checker to use them for structural sub-typing. If it could, that would be the simplest and most elegant solution. Otherwise, could we leave this out for now and look how we could make it work with existing protocol solutions like Zope interfaces? |
Jukka, your typing.py has a Protocol class that is more complex than most other infrastructure, and it is used for those generic ABCs (e.g. Sized, Hashable) whose collections.abc counterpart does a runtime structural check. Does this mean that you have now implemented this in mypy? I kind of like the resulting syntax for defining a generic ABC that is supposed to use structural testing:
(Note that Sized is not generic, but Container is.) Łukasz: I have no experience with zope interfaces. But perhaps they are easy enough to parse for a hypothetical static type checker, and they feel close enough to types/classes that we should try to support them? Can you sketch a simple example? |
Quoting Łukasz in python/mypy#539 (comment):
etc. etc. As ABCs are built-in, it seems natural to suggest that they should be used for defining interfaces. However, I understand that static analysis (so, the type checker) might not be able to process every abstract class in general. |
Guido, the machinery in mypy's typing.py has been there for a long time, but it was never fully implemented, and in particular, the type checker doesn't know anything about this. It shouldn't really be there -- everything should just use ABCs, until there is type system support for protocols. I added a new task for removing If protocols (or something similar) will be included in the PEP, I'll update the above issue. |
So we have multiple competing ways to spell protocols, but no way to type-check them statically. This feels like something we'll have to leave out of the PEP and come back to later in a separate PEP. |
OK, let's remove Protocol from typing.py for now. We should come back to it in a separate PEP, especially that PEP 245 has been rejected and there are ABCs now. As for Zope interfaces, an interface definition is easy to parse, except for invariants (runnable checks, similar to instancechecks in ABCs). We have to bear in mind that existing interface definitions may sometimes be dynamic, it's Python. That's fine, I think. What the type checker doesn't know, it assumes it's correct. I see two issues with Zope interfaces:
|
Closing as this is being left out for now. |
Reopening this, as duck typing is important according to the BDFL-Delegate (Mark Shannon). |
I'm writing here a bunch of related issues that came to mind. These are intended to start a discussion -- I try not to propose any course of action yet (though I have some opinions about some of these already). I'm sure there are other issues not covered by these. 1) What to call these types? We could call them at least duck types, protocols, interfaces or structural types. I'll call them protocols below, but I'm not specifically advocating any particular term. 2) How to define an interface? Here are a few possible ways:
3) How to define a method in a protocol? A few possible ways:
4) How to define an attribute in a protocol? Some ideas:
We could also disallow recursive types (see below). 5) How to explicitly declare that a class conforms to a protocol? We may want the type checker to verify that a class actually conform to a protocol. Some ideas for this:
Alternatively, this can always be implicit. In that case we can do something like this
We also need to decide whether the subclass will inherit the method implementations defined in the body of the protocol class in case we use regular inheritance (assuming they aren't abstract but regular methods). If we use a class decorator, we'd probably don't inherit anything. 6) Should we support protocols extending protocols? We can probably use the same approach for defining a subprotocol as in (5). 7) Should we support optional methods and attributes? I think TypeScript supports the concept of optional methods. If we declare a method as optional, subtypes don't have to implement it, but if they implement, the signature should be compatible. This could also work with attributes, but coming up with a syntax may be tricky. 8) Should some ABCs in typing be protocols instead? For example, maybe 9) Should be support recursive protocols? For example, when 10) Should we support generic protocols? For example, if 11) Should these be interoperable with other similar implementations? See Guido's remark above. Maybe we should interoperate with Zope interfaces (or just borrow the implementation and include it in |
I'm sorry I brought up Zope Interfaces. I'll respond to the rest when I On Monday, May 18, 2015, Jukka Lehtosalo [email protected] wrote:
--Guido van Rossum (on iPad) |
Another open issue: 12) How should isinstance work? We could disallow |
IMO, following the same conventions as stubs would be easier and less confusing. |
I promised offline to write up a proposal for structural subtyping. This is the first iteration of one. If we reach a consensus, I can write this into a PEP. Motivation Currently,
My intention is that the above code could be written instead equivalently without explicit base classes in the class definition, and
As I mentioned earlier, there are many individual design decisions that we need to agree on. I'm proposing answers to many of them here. 1) What to call these types? Let's call them protocols. The reason is that the term iterator protocol, for example, is widely understood in the community, and coming up with a new term for this concept in a statically typed context would just create confusion. This has the drawback that the term 'protocol' becomes overloaded with two subtly different meanings: the first is the traditional, well-known but slightly fuzzy concept of protocols such as iterable; the second is the more explicitly defined concept of protocols in statically typed code (or more generally in code that just uses the 2) How to define and use a protocol? There would be a new class
Now if we define a class
Protocol types can be used in annotations, of course, and for type checking:
Note that both the user-defined class If using the current 3) How to define a method in a protocol? I propose that most of the regular rules for classes still apply to protocol classes (modulo a few exceptions that only apply to protocol classes). I'd like protocols to also be ABCs, so all of these would be valid within a protocol class:
For variants 1, 2 and 3, a type checker should probably always require an explicit implementation to be defined in a subclass that explicitly subclasses the protocol (see below for more about this), because the implementations return I also propose that a 4) How to define a data attribute in a protocol? Similar to 3), there will be multiple valid ways of defining data attributes (or properties). All of these will be valid:
Also, properties with setters can also be defined. The first three variants would be the recommended ways of defining attributes or properties, but similar to 3), the others are possible and may be useful for supporting legacy code. When using an Attributes should not be defined in the body of a method by assignment via When using the
If we'd use a I'm not sure sure what to do with Overall, I'm probably the least happy about this part of the proposal. 5) How to explicitly declare that a class conforms to a protocol? I propose that protocols can be used as regular base classes. I can see at least three things that support this decision. First, a protocol class could define default implementations for some methods ( Note that subclassing a protocol class would not turn the subclass into a protocol unless it also has Some terminology could be useful here for clarity. If a class includes a protocol in its MRO, the class is an (explicit) subclass of the procotol. If a class ia a structural subtype of a protocol, it is said to implement the protocol and to be compatible with a protocol. If a class is compatible with a protocol but the protocol is not included in the MRO, the class is an implicit subclass of the protocol. We could also explicitly add an assignment for checking that a class implements a protocol. I've seen a similar pattern in some Go code that I've reviewed. Example:
I don't much care above the above example, as it moves the check away from the class definition and it almost requires a comment as otherwise the code probably wouldn't make any sense to an average reader -- it looks like dead code. Besides, in the simplest form it requires us to construct an instance of 6) Should we support protocols extending protocols? I think that we should support subprotocols. A subprotocol can be defined by having both one or more protocols as the explicit base classes and also having
Now the protocol
The two definitions of If we omit 7) Should we support optional attributes? We can come up with examples where it would be handy to be able to say that a method or data attribute does not need to be present in a class implementing a protocol, but if it's present, it must conform to a specific signature or type. One could use a In the interest of simplicity, let's not support optional methods or attributes. We can always revisit this later if there is an actual need. The current realistic potential use cases for protocols that I've seen don't require these. However, other languages have similar features and apparently they are pretty commonly used. If I remember correctly, at least TypeScript and Objective-C support a similar concept. 8) Should some ABCs in typing be protocols instead? I think that at least these classes in
These classes are small and conceptually simple. It's easy to see which of these protocols a class implements from the presence of a small number of magic methods, which immediately suggest a protocol. I'm not sure about other classes such as 9) Should we support recursive protocols? Sure, why not. They might useful for representing self-referential data structures like trees in an abstract fashion, but I don't see them used commonly in production code. 10) Should we support generic protocols? Generic protocol are important. For example,
11) Should these be interoperable with other similar implementations? The protocols as described here are basically a small extension to the existing concept of ABCs. I argue that this is the way they should be understood, instead of as something that replaces Zope interfaces, for example. 12) How should We shouldn't implement any magic My preferred semantics would be to make However, it should be possible for protocol types to implement custom 13) Should every class be a protocol by default? Some languages such as Go make structural subtyping the only or the primary form of subtyping. We could achieve a similar result by making all classes protocols by default (or even always). I argue that this would be a bad idea and classes should need to be explicitly marked as protocols, as shown in my proposal above. Here's my rationale:
14) Should protocols be introspectable? The existing introspection machinery ( As all attributes need to be defined in the class body based on this proposal, protocol classes would have better support for introspection than regular classes where attributes can be defined implicitly -- protocol attributes can't be initialized in ways that are not visible to introspection (using 15) How would We'd need to implement at least the following things:
|
I like almost all of this. Let's take this to python-ideas now! I have a few nits and questions, but they're not important enough to wait, and they're not very deep. (There's something niggling about making e.g. Sized a Protocol and not implementing isinstance(), since collections.abc.Sized does implement it.) |
FYI, I posted a link to the latest proposal to python-ideas. Crosslink: https://mail.python.org/pipermail/python-ideas/2015-September/035859.html |
Sorry, the description of |
@lyschoening
there will be no any changes in the interpreter, most of the things we discuss are only for type checker. But I think I understand what you want, we are going to update the PEP draft, so that you will not need to put @brettcannon class Proto(Protocol):
@abstractmethod
def method(self) -> Iterable[int]: # this should not be an error, although formally it is
raise NotImplementedError # since we don't return an 'Iterable'. |
What I meant was that all protocol members required (I am not advocating that they should be usable in the same way as other ABCs, but that appeared to be the goal.) |
This adds static support for structural subtyping. Previous discussion is here python/typing#11 Fixes #222
I've reviewed the current draft of PEP 544 at PyCon US 2017 sprints. It looks good from PyCharm's perspective. @JukkaL was kind to answer some questions during my review. The only remaining issue is we would like protocols to be available earlier than Python 3.7, perhaps, as a part of |
Yes on that last question. We also want in for Python 2.7. |
@vlasovskikh I just posted the latest version of the PEP on python-dev https://mail.python.org/pipermail/python-dev/2017-May/148005.html |
This is an incredible feature and I'm very eager to use it. Just curious when the current "pseudo-protocols" like |
@ilevkivskyi I was searching the wrong repos! Thanks. |
Most likely those will show up in typing_extensions first, unless we can
convince ourselves that there's no downside of rushing those into the
Python 3.7 release (and then freezing them for ~2 years).
…On Wed, Nov 1, 2017 at 11:04 AM, Chad Dombrova ***@***.***> wrote:
This is an incredible feature and I'm very eager to use it. Just curious
when the current "pseudo-protocols" like Iterable will be converted to
typing_extensions.Protocols (mentioned here
<https://www.python.org/dev/peps/pep-0544/#changes-in-the-typing-module>
in the pep) so that I can begin using them with mypy. Will they land in
typing or show up in typing_extensions first?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#11 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ACwrMn9a7QL85lkBWcz9z7IDEPLO18aNks5syLKugaJpZM4CvSAy>
.
--
--Guido van Rossum (python.org/~guido)
|
@gvanrossum no changes needed in |
I'm not sure how you can say that class C:
def __iter__(self) -> Iterator[int]:
yield 0
for x in C(): # error: Iterable expected
print(x) This complains that a |
|
Ah, cool. So the concerns are more about protocols that *don't* do that,
e.g. Sequence or Mapping.
|
Yes, there are four classes |
I like that as a first step forward!
|
Hi, are there any updates on the remaining collections which are not yet protocols -- |
The plan is to keep them as they are.
…On Sun, Apr 22, 2018, 22:30 Chad Dombrova ***@***.***> wrote:
Hi, are there any updates on the remaining collections which are not yet
protocols -- Sequence, Mapping, MutableSequence, and MutableMapping?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#11 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ACwrMuvI2i6OCmcBEm16b8ZyLI8mR5tcks5trWbkgaJpZM4CvSAy>
.
|
What's the reasoning behind that? Even if the abc classes don't become protocols, it would be convenient to have |
We have no options now. The feature cut-off for Python 3.7 has passed long ago, and this festure would require a runtime change in Strong -1 on We had a discussion over e-mail with @gvanrossum recently, we are both +0 on making these protocols (only +0 mostly because they are large, while protocols should be more compact), so we can consider this again in one year for Python 3.8. |
I have some concerns about turning Also, currently Examples of somewhat tricky signatures in @overload # Overloads are a somewhat tricky feature
def __getitem__(self, i: int) -> _T_co: ...
@overload
def __getitem__(self, s: slice) -> Sequence[_T_co]: ...
def __contains__(self, x: object) -> bool: ... # object argument type may be unexpected |
@JukkaL actually yes, I have seen someone on gitter recently confused by |
@jakebailey (MS Language Server representative) |
PEP 544 is now accepted, mypy (and some other type checkers) have good support for the PEP, all necessary infrastructure updates have been made. So I am finally closing this issue as fixed. |
|
In mypy's typing,py there's a neat feature called Protocol. Perhaps we should add this to the PEP. Or perhaps it's similar to a Python ABC?
The text was updated successfully, but these errors were encountered: