Work in many email responses.

This commit is contained in:
Guido van Rossum 2007-04-20 22:26:10 +00:00
parent 69064d13b6
commit 66d4f339be
1 changed files with 136 additions and 80 deletions

View File

@ -39,9 +39,9 @@ makes a set", "what makes a mapping" and "what makes a sequence".
Acknowledgements Acknowledgements
---------------- ----------------
Talin wrote the Rationale below [1]_. For that alone he deserves Talin wrote the Rationale below [1]_ as well as most of the section on
co-authorship. But the rest of the PEP uses "I" referring to the ABCs vs. Interfaces. For that alone he deserves co-authorship. But
first author. the rest of the PEP uses "I" referring to the first author.
Rationale Rationale
@ -55,54 +55,58 @@ Invocation means interacting with an object by invoking its methods.
Usually this is combined with polymorphism, so that invoking a given Usually this is combined with polymorphism, so that invoking a given
method may run different code depending on the type of an object. method may run different code depending on the type of an object.
Inspection means the ability for external code (outside of the object's Inspection means the ability for external code (outside of the
methods) to examine the type or properties of that object, and make object's methods) to examine the type or properties of that object,
decisions on how to treat that object based on that information. and make decisions on how to treat that object based on that
information.
Both usage patterns serve the same general end, which is to be able to Both usage patterns serve the same general end, which is to be able to
support the processing of diverse and potentially novel objects in a support the processing of diverse and potentially novel objects in a
uniform way, but at the same time allowing processing decisions to be uniform way, but at the same time allowing processing decisions to be
customized for each different type of object. customized for each different type of object.
In classical OOP theory, invocation is the preferred usage pattern, and In classical OOP theory, invocation is the preferred usage pattern,
inspection is actively discouraged, being considered a relic of an and inspection is actively discouraged, being considered a relic of an
earlier, procedural programming style. However, in practice this view is earlier, procedural programming style. However, in practice this view
simply too dogmatic and inflexible, and leads to a kind of design is simply too dogmatic and inflexible, and leads to a kind of design
rigidity that is very much at odds with the dynamic nature of a language rigidity that is very much at odds with the dynamic nature of a
like Python. language like Python.
In particular, there is often a need to process objects in a way that In particular, there is often a need to process objects in a way that
wasn't anticipated by the creator of the object class. It is not always wasn't anticipated by the creator of the object class. It is not
the best solution to build in to every object methods that satisfy the always the best solution to build in to every object methods that
needs of every possible user of that object. Moreover, there are many satisfy the needs of every possible user of that object. Moreover,
powerful dispatch philosophies that are in direct contrast to the there are many powerful dispatch philosophies that are in direct
classic OOP requirement of behavior being strictly encapsulated within contrast to the classic OOP requirement of behavior being strictly
an object, examples being rule or pattern-match driven logic. encapsulated within an object, examples being rule or pattern-match
driven logic.
On the the other hand, one of the criticisms of inspection by classic On the the other hand, one of the criticisms of inspection by classic
OOP theorists is the lack of formalisms and the ad hoc nature of what is OOP theorists is the lack of formalisms and the ad hoc nature of what
being inspected. In a language such as Python, in which almost any is being inspected. In a language such as Python, in which almost any
aspect of an object can be reflected and directly accessed by external aspect of an object can be reflected and directly accessed by external
code, there are many different ways to test whether an object conforms code, there are many different ways to test whether an object conforms
to a particular protocol or not. For example, if asking 'is this object to a particular protocol or not. For example, if asking 'is this
a mutable sequence container?', one can look for a base class of 'list', object a mutable sequence container?', one can look for a base class
or one can look for a method named '__getitem__'. But note that although of 'list', or one can look for a method named '__getitem__'. But note
these tests may seem obvious, neither of them are correct, as one that although these tests may seem obvious, neither of them are
generates false negatives, and the other false positives. correct, as one generates false negatives, and the other false
positives.
The generally agreed-upon remedy is to standardize the tests, and group The generally agreed-upon remedy is to standardize the tests, and
them into a formal arrangement. This is most easily done by associating group them into a formal arrangement. This is most easily done by
with each class a set of standard testable properties, either via the associating with each class a set of standard testable properties,
inheritance mechanism or some other means. Each test carries with it a either via the inheritance mechanism or some other means. Each test
set of promises: it contains a promise about the general behavior of the carries with it a set of promises: it contains a promise about the
class, and a promise as to what other class methods will be available. general behavior of the class, and a promise as to what other class
methods will be available.
This PEP proposes a particular strategy for organizing these tests known This PEP proposes a particular strategy for organizing these tests
as Abstract Base Classes, or ABC. ABCs are simply Python classes that known as Abstract Base Classes, or ABC. ABCs are simply Python
are added into an object's inheritance tree to signal certain features classes that are added into an object's inheritance tree to signal
of that object to an external inspector. Tests are done using certain features of that object to an external inspector. Tests are
isinstance(), and the presence of a particular ABC means that the test done using isinstance(), and the presence of a particular ABC means
has passed. that the test has passed.
Like all other things in Python, these promises are in the nature of a Like all other things in Python, these promises are in the nature of a
gentlemen's agreement - which means that the language does not attempt gentlemen's agreement - which means that the language does not attempt
@ -203,8 +207,7 @@ One Trick Ponies
'''''''''''''''' ''''''''''''''''
These abstract classes represent single methods like ``__iter__`` or These abstract classes represent single methods like ``__iter__`` or
``__len__``. The ``Iterator`` class is included as well, even though ``__len__``.
it has two prescribed methods.
``Hashable`` ``Hashable``
The base class for classes defining ``__hash__``. The The base class for classes defining ``__hash__``. The
@ -221,12 +224,12 @@ it has two prescribed methods.
never change their value (as compared by ``==``) or their hash never change their value (as compared by ``==``) or their hash
value. If a class cannot guarantee this, it should not derive value. If a class cannot guarantee this, it should not derive
from ``Hashable``; if it cannot guarantee this for certain from ``Hashable``; if it cannot guarantee this for certain
instances only, ``__hash__`` for those instances should raise an instances only, ``__hash__`` for those instances should raise a
exception. ``TypeError`` exception.
Note: being an instance of this class does not imply that an Note: being an instance of this class does not imply that an
object is immutable; e.g. a tuple containing a list as a member is object is immutable; e.g. a tuple containing a list as a member is
not immutable; its ``__hash__`` method raises an exception. not immutable; its ``__hash__`` method raises ``TypeError``.
``Iterable`` ``Iterable``
The base class for classes defining ``__iter__``. The The base class for classes defining ``__iter__``. The
@ -252,11 +255,11 @@ it has two prescribed methods.
too cute), ``Countable`` (the set of natural numbers is a too cute), ``Countable`` (the set of natural numbers is a
countable set in math), ``Enumerable`` (sounds like a sysnonym for countable set in math), ``Enumerable`` (sounds like a sysnonym for
``Iterable``), ``Dimension``, ``Extent`` (sound like numbers to ``Iterable``), ``Dimension``, ``Extent`` (sound like numbers to
me). me), ``Bounded`` (probably just as confusing as ``Fininte``).
``Container`` ``Container``
The base class for classes defining ``__contains__``. The The base class for classes defining ``__contains__``. The
``__contains__`` method should return a ``bool``. The abstract ``__contains__`` method should return a ``bool``. The abstract
``__contains__`` method returns ``False``. **Invariant:** If a ``__contains__`` method returns ``False``. **Invariant:** If a
class ``C`` derives from ``Container`` as well as from class ``C`` derives from ``Container`` as well as from
``Iterable``, then ``(x in o for x in o)`` should be a generator ``Iterable``, then ``(x in o for x in o)`` should be a generator
@ -272,7 +275,23 @@ it has two prescribed methods.
of the same type as the method's target) intead of an element. of the same type as the method's target) intead of an element.
For now, I'm using the same type for all three. This means that For now, I'm using the same type for all three. This means that
is is possible for ``x in o`` to be True even though ``x`` is is is possible for ``x in o`` to be True even though ``x`` is
never yielded by ``iter(o)``. never yielded by ``iter(o)``. A suggested name for the third form
is ``Searchable``.
``PartiallyOrdered``
This ABC defines the 4 inequality operations ``<``, ``<=``, ``>=``,
``>``. (Note that ``==`` and ``!=`` are defined by ``object``.)
Classes deriving from this ABC should satisfy weak invariants such
as ``a < b < c`` implies ``a < c`` but don't require that for any
two instances ``x`` and ``y`` exactly one of ``x < y``, ``x == y``
or ``x >= y`` apply.
``TotallyOrdered``
This ABC derives from ``PartiallyOrdered``. It adds no new
operations but implies a promise of stronger invariants. **Open
issues:** Should ``float`` derive from ``TotallyOrdered`` even
though for ``NaN`` this isn't strictly correct?
Sets Sets
@ -302,12 +321,14 @@ general without using a symbolic algebra package. So I consider this
out of the scope of a pragmatic proposal like this. out of the scope of a pragmatic proposal like this.
``Set`` ``Set``
This is a finite, iterable container, i.e. a subclass of
``Sized``, ``Iterable`` and ``Container``. Not every subset of This is a finite, iterable, partially ordered container, i.e. a
those three classes is a set though! Sets have the additional subclass of ``Sized``, ``Iterable``, ``Container`` and
invariant that each element occurs only once (as can be determined ``PartiallyOrdered``. Not every subset of those three classes is
by iteration), and in addition sets define concrete operators that a set though! Sets have the additional invariant that each
implement rich comparisons defined as subclass/superclass tests. element occurs only once (as can be determined by iteration), and
in addition sets define concrete operators that implement the
inequality operations as subclass/superclass tests.
Sets with different implementations can be compared safely, Sets with different implementations can be compared safely,
efficiently and correctly. Because ``Set`` derives from efficiently and correctly. Because ``Set`` derives from
@ -325,8 +346,7 @@ out of the scope of a pragmatic proposal like this.
type in Python 2 are not supported, as these are mostly just type in Python 2 are not supported, as these are mostly just
aliases for ``__le__`` and ``__ge__``. aliases for ``__le__`` and ``__ge__``.
**Open issues:** Should I spell out the invariants and method **Open issues:** Spell out the invariants and method definitions.
definitions?
``ComposableSet`` ``ComposableSet``
This is a subclass of ``Set`` that defines abstract operators to This is a subclass of ``Set`` that defines abstract operators to
@ -360,42 +380,45 @@ out of the scope of a pragmatic proposal like this.
from Composable)? from Composable)?
``MutableSet`` ``MutableSet``
This is a subclass of ``ComposableSet`` implementing additional This is a subclass of ``ComposableSet`` implementing additional
operations to add and remove elements. The supported methods have operations to add and remove elements. The supported methods have
the semantics known from the ``set`` type in Python 2: the semantics known from the ``set`` type in Python 2 (except
for ``discard``, which is modeled after Java):
``.add(x)`` ``.add(x)``
Abstract method that adds the element ``x``, if it isn't Abstract method returning a ``bool`` that adds the element
already in the set. ``x`` if it isn't already in the set. It should return
``True`` if ``x`` was added, ``False`` if it was already
``.remove(x)`` there. The abstract implementation raises
Abstract method that removes the element ``x``; raises ``NotImplementedError``.
``KeyError`` if ``x`` is not in the set.
``.discard(x)`` ``.discard(x)``
Concrete method that removes the element ``x`` if it is Abstract method returning a ``bool`` that removes the element
a member of the set; implemented using ``__contains__`` ``x`` if present. It should return ``True`` if the element
and ``remove``. was present and ``False`` if it wasn't. The abstract
implementation raises ``NotImplementedError``.
``.clear()`` ``.clear()``
Abstract method that empties the set. (Making this concrete Abstract method that empties the set. The abstract
would just add a slow, cumbersome default implementation.) implementation raises ``NotImplementedError``. (Making this
concrete would just add a slow, cumbersome default
implementation.)
``.pop()`` ``.pop()``
Concrete method that removes an arbitrary item. If the set is Concrete method that removes an arbitrary item. If the set is
empty, it raises ``KeyError``. The default implementation empty, it raises ``KeyError``. The default implementation
removes the first item returned by the set's iterator. removes the first item returned by the set's iterator.
This also supports the in-place mutating operations ``|=``, ``.toggle(x)``
``&=``, ``^=``, ``-=``. It does not support the named methods Concrete method returning a ``bool`` that adds x to the set if
that perform (almost) the same operations, like ``update``, even it wasn't there, but removes it if it was there. It should
though these don't have exactly the same rules (``update`` takes return ``True`` if ``x`` was added, ``False`` if it was
any iterable, while ``|=`` requires a set). removed.
**Open issues:** Should we unify ``remove`` and ``discard``, a la This also supports the in-place mutating operations ``|=``,
Java (which has a single method returning a boolean indicating ``&=``, ``^=``, ``-=``. These are concrete methods whose right
whether it was removed or not)? operand can be an arbitrary ``Iterable``. It does not support the
named methods that perform (almost) the same operations.
Mappings Mappings
@ -449,16 +472,19 @@ The built-in type ``dict`` derives from ``MutableMapping``.
``MutableMapping`` ``MutableMapping``
A subclass of ``Mapping`` that also implements some standard A subclass of ``Mapping`` that also implements some standard
mutating methods. Abstract methods include ``__setitem__``, mutating methods. Abstract methods include ``__setitem__``,
``__delitem__``, ``clear``, ``update``. Concrete methods include ``__delitem__``, ``clear``, ``update``. Concrete methods include
``pop``, ``popitem``. Note: ``setdefault`` is *not* included. ``pop``, ``popitem``. Note: ``setdefault`` is *not* included.
**Open issues:**
* Do we need BasicMapping and IterableMapping? We should probably * Do we need BasicMapping and IterableMapping? We should probably
just start with Mapping. just start with Mapping.
* We should say more about mapping view types. * We should say more about mapping view types.
* Should we add the ``copy`` method?
Sequences Sequences
''''''''' '''''''''
@ -501,13 +527,27 @@ from ``HashableSequence``.
instances. instances.
Strings
-------
Python 3000 has two built-in string types: byte strings (``bytes``),
deriving from ``MutableSequence``, and (Unicode) character strings
(``str``), deriving from ``HashableSequence``.
**Open issues:** define the base interfaces for these so alternative
implementations and subclasses know what they are in for. This may be
the subject of a new PEP or PEPs (maybe PEP 358 can be co-opted for
the ``bytes`` type).
ABCs for Numbers ABCs for Numbers
---------------- ----------------
**Open issues:** Define: Number, Complex, Real, Rational, Integer. Do **Open issues:** Define: ``Number``, ``Complex``, ``Real``,
we have a use case for Cardinal (Integer >= 0)? Do we need Index ``Rational``, ``Integer``. Maybe also ``Cardinal`` (``Integer`` >=
(converts to Integer using __index__)? Or is that just subsumed into 0)? We probably also need ``Index``, which converts to ``Integer``
Integer and should we use __index__ only at the C level? using ``__index__``. This should probably be moved out to a separate
PEP.
Guidelines for Writing ABCs Guidelines for Writing ABCs
@ -595,6 +635,22 @@ of the work that went into e.g. defining the various shades of
"mapping-ness" and the nomenclature could easily be adapted for a "mapping-ness" and the nomenclature could easily be adapted for a
proposal to use Interfaces instead of ABCs. proposal to use Interfaces instead of ABCs.
"Interfaces" in this context refers to a set of proposals for
additional metadata elements attached to a class which are not part of
the regular class hierarchy, but do allow for certain types of
inheritance testing.
Such metadata would be designed, at least in some proposals, so as to
be easily mutable by an application, allowing application writers to
override the normal classification of an object.
The drawback to this idea of attaching mutable metadata to a class is
that classes are shared state, and mutating them may lead to conflicts
of intent. Additionally, the need to override the classification of
an object can be done more cleanly using generic functions: In the
simplest case, one can define a "category membership" generic function
that simply returns False in the base implementation, and then provide
overrides that return True for any classes of interest.
Open Issues Open Issues
=========== ===========