Write PEP 3119 section
This commit is contained in:
parent
3b1202dfe8
commit
2185241a63
|
@ -807,41 +807,142 @@ constraints upon the memory returned. Some examples are:
|
||||||
PEP 3119: Abstract Base Classes
|
PEP 3119: Abstract Base Classes
|
||||||
=====================================================
|
=====================================================
|
||||||
|
|
||||||
XXX write this -- this section is currently just brief notes.
|
Some object-oriented languages such as Java support interfaces: declarations
|
||||||
|
that a class has a given set of methods or supports a given access protocol.
|
||||||
|
Abstract Base Classes (or ABCs) are an equivalent feature for Python. The ABC
|
||||||
|
support consists of an :mod:`abc` module containing a metaclass called
|
||||||
|
:class:`ABCMeta`, special handling
|
||||||
|
of this metaclass by the :func:`isinstance` and :func:`issubclass` built-ins,
|
||||||
|
and a collection of basic ABCs that the Python developers think will be widely
|
||||||
|
useful.
|
||||||
|
|
||||||
How to identify a file object?
|
Let's say you have a particular class and wish to know whether it supports
|
||||||
|
dictionary-style access. The phrase "dictionary-style" is vague, however.
|
||||||
|
It probably means that accessing items with ``obj[1]`` works.
|
||||||
|
Does it imply that setting items with ``obj[2] = value`` works?
|
||||||
|
Or that the object will have :meth:`keys`, :meth:`values`, and :meth:`items`
|
||||||
|
methods? What about the iterative variants such as :meth:`iterkeys`? :meth:`copy`
|
||||||
|
and :meth:`update`? Iterating over the object with :func:`iter`?
|
||||||
|
|
||||||
ABCs are a collection of classes describing various interfaces.
|
Python 2.6 includes a number of different ABCs in the :mod:`collections`
|
||||||
Classes can derive from an ABC to indicate they support that ABC's
|
module. :class:`Iterable` indicates that a class defines :meth:`__iter__`,
|
||||||
interface. Concrete classes should obey the semantics specified by
|
and :class:`Container` means the class supports ``x in y`` expressions
|
||||||
an ABC, but Python can't check this; it's up to the implementor.
|
by defining a :meth:`__contains__` method. The basic dictionary interface of
|
||||||
|
getting items, setting items, and
|
||||||
|
:meth:`keys`, :meth:`values`, and :meth:`items`, is defined by the
|
||||||
|
:class:`MutableMapping` ABC.
|
||||||
|
|
||||||
A metaclass lets you declare that an existing class or type
|
You can derive your own classes from a particular ABC
|
||||||
derives from a particular ABC. You can even
|
to indicate they support that ABC's interface::
|
||||||
|
|
||||||
class AppendableSequence:
|
import collections
|
||||||
|
|
||||||
|
class Storage(collections.MutableMapping):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
Alternatively, you could write the class without deriving from
|
||||||
|
the desired ABC and instead register the class by
|
||||||
|
calling the ABC's :meth:`register` method::
|
||||||
|
|
||||||
|
import collections
|
||||||
|
|
||||||
|
class Storage:
|
||||||
|
...
|
||||||
|
|
||||||
|
collections.MutableMapping.register(Storage)
|
||||||
|
|
||||||
|
For classes that you write, deriving from the ABC is probably clearer.
|
||||||
|
The :meth:`register` method is useful when you've written a new
|
||||||
|
ABC that can describe an existing type or class, or if you want
|
||||||
|
to declare that some third-party class implements an ABC.
|
||||||
|
For example, if you defined a :class:`PrintableType` ABC,
|
||||||
|
it's legal to do:
|
||||||
|
|
||||||
|
# Register Python's types
|
||||||
|
PrintableType.register(int)
|
||||||
|
PrintableType.register(float)
|
||||||
|
PrintableType.register(str)
|
||||||
|
|
||||||
|
Classes should obey the semantics specified by an ABC, but
|
||||||
|
Python can't check this; it's up to the class author to
|
||||||
|
understand the ABC's requirements and to implement the code accordingly.
|
||||||
|
|
||||||
|
To check whether an object supports a particular interface, you can
|
||||||
|
now write::
|
||||||
|
|
||||||
|
def func(d):
|
||||||
|
if not isinstance(d, collections.MutableMapping):
|
||||||
|
raise ValueError("Mapping object expected, not %r" % d)
|
||||||
|
|
||||||
|
(Don't feel that you must now begin writing lots of checks as in the
|
||||||
|
above example. Python has a strong tradition of duck-typing, where
|
||||||
|
explicit type-checking isn't done and code simply calls methods on
|
||||||
|
an object, trusting that those methods will be there and raising an
|
||||||
|
exception if they aren't. Be judicious in checking for ABCs
|
||||||
|
and only do it where it helps.)
|
||||||
|
|
||||||
|
You can write your own ABCs by using ``abc.ABCMeta`` as the
|
||||||
|
metaclass in a class definition::
|
||||||
|
|
||||||
|
from abc import ABCMeta
|
||||||
|
|
||||||
|
class Drawable():
|
||||||
__metaclass__ = ABCMeta
|
__metaclass__ = ABCMeta
|
||||||
|
|
||||||
AppendableSequence.register(list)
|
def draw(self, x, y, scale=1.0):
|
||||||
assert issubclass(list, AppendableSequence)
|
pass
|
||||||
assert isinstance([], AppendableSequence)
|
|
||||||
|
|
||||||
@abstractmethod decorator -- you can't instantiate classes w/
|
def draw_doubled(self, x, y):
|
||||||
an abstract method.
|
self.draw(x, y, scale=2.0)
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
@abstractproperty decorator
|
class Square(Drawable):
|
||||||
|
def draw(self, x, y, scale):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
In the :class:`Drawable` ABC above, the :meth:`draw_doubled` method
|
||||||
|
renders the object at twice its size and can be implemented in terms
|
||||||
|
of other methods described in :class:`Drawable`. Classes implementing
|
||||||
|
this ABC therefore don't need to provide their own implementation
|
||||||
|
of :meth:`draw_doubled`, though they can do so. An implementation
|
||||||
|
of :meth:`draw` is necessary, though; the ABC can't provide
|
||||||
|
a useful generic implementation. You
|
||||||
|
can apply the ``@abstractmethod`` decorator to methods such as
|
||||||
|
:meth:`draw` that must be implemented; Python will
|
||||||
|
then raise an exception for classes that
|
||||||
|
don't define the method::
|
||||||
|
|
||||||
|
class Drawable():
|
||||||
|
__metaclass__ = ABCMeta
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def draw(self, x, y, scale):
|
||||||
|
pass
|
||||||
|
|
||||||
|
Note that the exception is only raised when you actually
|
||||||
|
try to create an instance of a subclass without the method::
|
||||||
|
|
||||||
|
>>> s=Square()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<stdin>", line 1, in <module>
|
||||||
|
TypeError: Can't instantiate abstract class Square with abstract methods draw
|
||||||
|
>>>
|
||||||
|
|
||||||
|
Abstract data attributes can be declared using the ``@abstractproperty`` decorator::
|
||||||
|
|
||||||
@abstractproperty
|
@abstractproperty
|
||||||
def readonly(self):
|
def readonly(self):
|
||||||
return self._x
|
return self._x
|
||||||
|
|
||||||
|
Subclasses must then define a :meth:`readonly` property
|
||||||
|
|
||||||
.. seealso::
|
.. seealso::
|
||||||
|
|
||||||
:pep:`3119` - Introducing Abstract Base Classes
|
:pep:`3119` - Introducing Abstract Base Classes
|
||||||
PEP written by Guido van Rossum and Talin.
|
PEP written by Guido van Rossum and Talin.
|
||||||
Implemented by XXX.
|
Implemented by Guido van Rossum.
|
||||||
Backported to 2.6 by Benjamin Aranguren, with Alex Martelli.
|
Backported to 2.6 by Benjamin Aranguren, with Alex Martelli.
|
||||||
|
|
||||||
.. ======================================================================
|
.. ======================================================================
|
||||||
|
|
Loading…
Reference in New Issue