Skip to content

PEP 544: Protocols #224

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

Merged
merged 40 commits into from
Mar 18, 2017
Merged
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3fa3e48
Collect various ideas
ilevkivskyi Mar 5, 2017
51b0ca0
Some formatting and reordering
ilevkivskyi Mar 5, 2017
993f7b3
Add some examples
ilevkivskyi Mar 5, 2017
e384336
Add planned link templates
ilevkivskyi Mar 6, 2017
7ea5d41
Add links + minor changes
Mar 6, 2017
cdcf62f
Polishing rationale
Mar 6, 2017
1ffed9b
Some more reshuffling and formatting
Mar 6, 2017
72ceae6
Add more examples
Mar 6, 2017
6bea2e8
Add more examples to existing approaches
Mar 6, 2017
d5972c3
Typos, reordering, and few more details (backport)
ilevkivskyi Mar 6, 2017
57d375f
Update list of protocols in typing
ilevkivskyi Mar 6, 2017
9d4d685
Defining protocols plus minor changes and formatting
ilevkivskyi Mar 7, 2017
82258d5
Explicitly declaring implementation and other changes
ilevkivskyi Mar 7, 2017
5d9fb7c
More polishing
ilevkivskyi Mar 7, 2017
a6e6d9e
Edit rejected/postponed ideas
ilevkivskyi Mar 7, 2017
3175013
Runtime things, reorder links
ilevkivskyi Mar 7, 2017
cbff669
Runtime decorator
ilevkivskyi Mar 7, 2017
dfccd06
Backward compatible part and last bits
Mar 8, 2017
60f4d52
Some clarifications
ilevkivskyi Mar 9, 2017
60e7f7f
Add links in text
ilevkivskyi Mar 9, 2017
c90aa1c
Caption style, add cross-refs
Mar 9, 2017
b008de1
Remove redundant links; + minor changes
ilevkivskyi Mar 10, 2017
02cca5c
One more tiny change
ilevkivskyi Mar 10, 2017
7d89b6b
Merge remote-tracking branch 'upstream/master' into protocols
ilevkivskyi Mar 10, 2017
0f3732a
Copyediting changes
JelleZijlstra Mar 10, 2017
95fbf58
Merge pull request #1 from JelleZijlstra/patch-2
ilevkivskyi Mar 10, 2017
cb65bff
Rename PEP with a valid number to get the build running
ilevkivskyi Mar 10, 2017
817bf2f
Reflow to 79 characters
ilevkivskyi Mar 10, 2017
2d89ba9
fix typo
JelleZijlstra Mar 10, 2017
0efcbff
Some grammar tweaks
brettcannon Mar 10, 2017
ebd4b17
Merge pull request #3 from brettcannon/patch-1
ilevkivskyi Mar 10, 2017
0de36be
Implement Guido's idea of EIBTI plus minor comments
ilevkivskyi Mar 11, 2017
767c58b
Fix typo
ilevkivskyi Mar 11, 2017
efc3154
Make implementation enforcement optional; fix order of Protocolbase
ilevkivskyi Mar 12, 2017
7d714c3
Add missing @abstractmethod decorators
ilevkivskyi Mar 13, 2017
d4ab050
Minor clarification
ilevkivskyi Mar 13, 2017
d9d21c2
Implement Jukka's and David's comments; few more minor things
ilevkivskyi Mar 16, 2017
4dfbfb2
Implement most comments by Łukasz; few more to do
ilevkivskyi Mar 17, 2017
d51420e
More changes in response to comments
Mar 18, 2017
f6240c8
Remove one reamining 'All'
ilevkivskyi Mar 18, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Some more reshuffling and formatting
  • Loading branch information
Ivan Levkivskyi committed Mar 6, 2017
commit 1ffed9b9a3318fc6942b5431b02007460bec5d3e
182 changes: 91 additions & 91 deletions pep-05xx.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,66 +94,67 @@ Existing approaches to structural subtyping
Before describing the actual specification, we review existing approaches to
structural subtyping in Python and other languges:

* Zope interfaces::
* Zope interfaces was probably historically first widely used approach to
structural subtyping. For example::

from zope.interface import Interface
from zope.interface import Attribute
from zope.interface import implements
from zope.interface import Interface
from zope.interface import Attribute
from zope.interface import implements

class IHost(Interface):
"""A host object"""
class IHost(Interface):
"""A host object"""

name = Attribute("Name of host")
name = Attribute("Name of host")

def goodmorning(guest):
"""Say good morning to guest"""
def goodmorning(guest):
"""Say good morning to guest"""

class Host(object):
class Host(object):

implements(IHost)
implements(IHost)

name = u''
name = u''

def goodmorning(self, guest):
return "Good morning, %s!" % guest
def goodmorning(self, guest):
return "Good morning, %s!" % guest

plus interface contracts/constraints::
plus interface contracts/constraints::

from zope.interface import invariant
from zope.interface import invariant

def contacts_invariant(obj):
if not (obj.email or obj.phone):
raise Exception("At least one contact info is required")
def contacts_invariant(obj):
if not (obj.email or obj.phone):
raise Exception("At least one contact info is required")

class IPerson(Interface):
class IPerson(Interface):

name = Attribute("Name")
email = Attribute("Email Address")
phone = Attribute("Phone Number")
name = Attribute("Name")
email = Attribute("Email Address")
phone = Attribute("Phone Number")

invariant(contacts_invariant)
invariant(contacts_invariant)

Even more is possible. However, this all requires runtime validation.
Even more is possible. However, this all requires runtime validation.

* TypeScript::
* TypeScript also provides support for user defined interfaces.
For example::

interface LabelledValue {
label: string;
}
interface LabelledValue {
label: string;
}

function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

In addition optional members, always errors on reduntant members.
In addition optional members, always errors on reduntant members.

* Go - everything is an interface
* Python abstract base classes

require explicit subclassing or registration.
require explicit subclassing or registration.

* Abstract classes in ``collections.abc`` (with ``__subclasshook__``)

Expand All @@ -164,8 +165,8 @@ Specification
Terminology
-----------

We propose to use the term *protocols* types supoorting structural subtyping.
The reason is that the term iterator protocol,
We propose to use the term *protocols* for types supoorting structural
subtyping. 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.
Expand Down Expand Up @@ -237,7 +238,7 @@ the implementation of the library. Besides, this would be uglier than how
you'd actually write the code in straight, idiomatic dynamically typed Python.
The code with a protocol class matches common Python conventions much better.
It's also automatically extensible and works with additional,
unrelated classes that happen to implement the required interface.
unrelated classes that happen to implement the required protocol.


Protocol members
Expand Down Expand Up @@ -473,11 +474,25 @@ Union[Proto.open() -> int, Proto.open() -> str],
although it is not a subclass of any of two.


``Type[...]`` with protocols
----------------------------

``Type[...]`` accepts only non-abstract (non-protocol?) classes

copy all stuff from my PR


``@runtime`` decorator
---------------------------

(like in ``collections.abc``), typechecker ensures no empty bodies


``isinstance()`` and narrowing types
------------------------------------

``isinstance(x, Proto)`` and ``issublclass(C, Proto)``
narrow type if defined with ``@auto_runtime``; ``isinstance(x, Proto[int])``
narrow type if defined with ``@runtime``; ``isinstance(x, Proto[int])``
always fails.

We shouldn't implement any magic ``isinstance()`` machinery, as performing
Expand All @@ -499,26 +514,11 @@ but this should be specific to these particular classes.
We need this fallback option anyway for backward compatibility.


``Type[...]`` with protocols
----------------------------

``Type[...]`` accepts only non-abstract (non-protocol?) classes

copy all stuff from my PR


Runtime behavior of protocol classes
====================================

At runtime normal ABCs, cannot be instantiated.


``@auto_runtime`` decorator
---------------------------

(like in ``collections.abc``), typechecker ensures no empty bodies


Changes in typing module
------------------------

Expand Down Expand Up @@ -572,22 +572,49 @@ Implementation details
We'd need to implement at least the following things:

* Define class ``Protocol`` (this could be simple, and would be similar
to ``Generic``).
to ``Generic``).
* Implement metaclass functionality to detect whether a class is
a protocol or not. Maybe add a class attribute such as ``__protocol__ = True``
if that's the case. Verify that a protocol class only has protocol
base classes in the MRO (except for object).
a protocol or not. Maybe add a class attribute such as ``__protocol__ = True``
if that's the case. Verify that a protocol class only has protocol
base classes in the MRO (except for object).
* Optionally, override ``isinstance``.
* Optionally, translate ``...`` class attribute values to
something else (properties?).
something else (properties?).


Postponed ideas
===============

Should we support optional attributes?
Make every class 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). However we believe that this would be
a bad idea and classes should need to be explicitly marked as protocols,
as shown in this proposal:

* Protocols don't have some properties of regular classes. In particular,
``isinstance`` is not well-defined for protocols, whereas it's well-defined
(and pretty commonly used) for regular classes.

* Protocol classes should generally not have (many) method implementations,
as they describe an interface, not an implementation.
Most classes have many implementations, making them bad protocol classes.

* Experience suggests that many classes are not practical as protocols anyway,
mainly because their interfaces are too large, complex or
implementation-oriented (for example, they may include de facto
private attributes and methods without a ``__`` prefix).
Most actually useful protocols in existing Python code seem to be implicit.
The ABCs in typing and ``collections.abc`` are a kind-of exception, but
even they are pretty recent additions to Python and most programmers
do not use them yet.


Support optional protocol members
---------------------------------

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
Expand All @@ -602,42 +629,15 @@ they are pretty commonly used. If I remember correctly, at least TypeScript
and Objective-C support a similar concept.


Should these be interoperable with other similar implementations?
-----------------------------------------------------------------
Should protocols be interoperable with 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.


Make every class 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.

* Protocols don't have some properties of regular classes. In particular,
``isinstance`` is not well-defined for protocols, whereas it's well-defined
(and pretty commonly used) for regular classes.

* Protocol classes should generally not have (many) method implementations,
as they describe an interface, not an implementation.
Most classes have many implementations, making them bad protocol classes.

* Experience suggests that most classes aren't practical as protocols anyway,
mainly because their interfaces are too large, complex or
implementation-oriented (for example, they may include de facto
private attributes and methods without a ``__`` prefix).
Most actually useful protocols in existing Python code seem to be implicit.
The ABCs in typing and ``collections.abc`` are a kind-of exception, but
even they are pretty recent additions to Python and most programmers
do not use them yet.


Use assignments to check explicitly that a class implements a protocol
----------------------------------------------------------------------

Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy