Skip to content

Commit 9f46223

Browse files
committed
Chapter 11 text.
1 parent ea96113 commit 9f46223

File tree

4 files changed

+470
-5
lines changed

4 files changed

+470
-5
lines changed

‎doc/source/10_trees_and_directed_acyclic_graphs.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ Glossary
11401140
single dispatch functions.
11411141

11421142
Exercises
1143-
--------
1143+
---------
11441144

11451145
Obtain the `skeleton code for these exercises from GitHub classroom
11461146
<https://classroom.github.com/a/DVDJ8r1y>`__. You should also update your clone

‎doc/source/11_further_object-oriented_features.rst

+336-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ This week we'll tie up a few loose ends by examining in detail some programming
55
concepts and Python features which we have encountered but not really studied
66
in the course so far.
77

8+
.. _decorators:
9+
810
Decorators
911
----------
1012

@@ -94,11 +96,11 @@ docstring for the original function, and not that of the decorator.
9496
Decorators which take arguments
9597
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9698

97-
Our :func:`make_at_expr` decorator doesn't have brackets after its name, and doesn't
98-
take any arguments. However :func:`functools.wrap` does have brackets, and takes a
99+
Our :func:`make_other_expr` decorator doesn't have brackets after its name, and doesn't
100+
take any arguments. However :func:`functools.wraps` does have brackets, and takes a
99101
function name as an argument. How does this work? The answer is yet another
100102
wrapper function. A decorator is a function which takes a function and
101-
returns a function. :func:`functools.wrap` takes an argument (it happens to be
103+
returns a function. :func:`functools.wraps` takes an argument (it happens to be
102104
a function but other decorators take other types) and returns a decorator
103105
function. That is, it is a function which takes in arguments and returns a
104106
function which takes a function and returns a function. It's functions all the
@@ -217,10 +219,266 @@ are features that a language can provide which makes the creation of useful
217219
abstract base classes easy. In Python, these features are provided by the
218220
:mod:`abc` module in the :ref:`Standard Library <library-index>`.
219221

222+
The :mod:`abc` module provides the :class:`~abc.ABC` class. This is itself an
223+
abstract base class: there is no need to ever make an object of type
224+
:class:`~abc.ABC`. Instead, classes inherit from :class:`~abc.ABC` in order to
225+
access features that it provides.
226+
227+
Abstract methods
228+
~~~~~~~~~~~~~~~~
229+
230+
Let's look back at the groups example from :numref:`Week %s <inheritance>`. We
231+
defined the base :class:`~example_code.groups.Group` class and specified that
232+
child classes had to implement the :meth:`_validate` and :meth:`operator`
233+
methods as well as the :attr:`symbol` :term:`class attribute`. But how should
234+
we actually know that these methods and attribute are required? This might be
235+
documented, but that is somewhat hit and miss: it is often less than
236+
completely obvious where to look for the documentation. Abstract methods
237+
provide a much more satisfactory solution to this problem. The
238+
:mod:`example_code.groups_abc` module is an update of the
239+
:mod:`example_code.groups` module which uses the :class:`~abc.ABC` class.
240+
241+
.. _groups_abc:
242+
243+
.. code-block:: python3
244+
:caption: An abstract base class version of the
245+
:class:`~example_code.groups_abc.Group` class. Note that the class itself
246+
inherits from :class:`~abc.ABC`, and the methods and attribute to be
247+
implemented by the :term:`child classes <child class>` have the
248+
`~abc.abstractmethod` decorator.
249+
:linenos:
250+
251+
from abc import ABC, abstractmethod
252+
253+
class Group(ABC):
254+
"""A base class containing methods common to many groups.
255+
256+
Each subclass represents a family of parametrised groups.
257+
258+
Parameters
259+
----------
260+
n: int
261+
The primary group parameter, such as order or degree. The
262+
precise meaning of n changes from subclass to subclass.
263+
"""
264+
265+
def __init__(self, n):
266+
self.n = n
267+
268+
@property
269+
@abstractmethod
270+
def symbol(self):
271+
"""Represent the group name as a character."""
272+
pass
273+
274+
@abstractmethod
275+
def _validate(self, value):
276+
"""Ensure that value is a legitimate element value in this group."""
277+
pass
278+
279+
@abstractmethod
280+
def operation(self, a, b):
281+
"""Return a ⊙ b using the group operation ⊙."""
282+
pass
283+
284+
def __call__(self, value):
285+
"""Create an element of this group."""
286+
return Element(self, value)
287+
288+
def __str__(self):
289+
"""Return a string in the form symbol then group parameter."""
290+
return f"{self.symbol}{self.n}"
291+
292+
def __repr__(self):
293+
"""Return the canonical string representation of the element."""
294+
return f"{type(self).__name__}({repr(self.n)})"
295+
296+
There are a few features of :numref:`groups_abc` which are noteworthy. First,
297+
observe that :class:`~example_code.groups_abc.Group` now inherits from
298+
:class:`~abc.ABC`. This simply enables the features that we will use next.
299+
The new :class:`~example_code.groups_abc.Group` class has
300+
:meth:`~example_code.groups_abc.Group._validate` and
301+
:meth:`~example_code.groups_abc.Group.operator` methods, but these don't
302+
actually do anything (their contents are merely :keyword:`pass`). They are,
303+
however, decorated with `~abc.abstractmethod`. The effect of this decorator can
304+
be observed if we try to instantiate :class:`~example_code.groups_abc.Group`:
305+
306+
.. code-block:: ipython3
307+
308+
In [1]: from example_code.groups_abc import Group
309+
310+
In [2]: Group(1)
311+
---------------------------------------------------------------------------
312+
TypeError Traceback (most recent call last)
313+
<ipython-input-2-76d67216101e> in <module>
314+
----> 1 Group(1)
315+
316+
TypeError: Can't instantiate abstract class Group with abstract methods _validate, operation, symbol
317+
318+
The combination of inheriting from :class:`~abc.ABC` and the
319+
`~abc.abstractmethod` decorator has the effect that instantiating this class is
320+
an error, and we are told why. We have skipped over the `symbol` attribute.
321+
There is no `abstractattribute` decorator, but the same effect can be achieved
322+
by creating an `~abc.abstractmethod` and converting it into a data attribute using
323+
`property`. In this case, the order of decorators is important:
324+
`~abc.abstractmethod` always needs to be the last, innermost, decorator.
325+
326+
The subclasses of :class:`~example_code.groups_abc.Group` that we defined,
327+
define all three of these attributes, so they can still be instantiated. For
328+
example:
329+
330+
.. code-block:: ipython3
331+
332+
In [1]: from example_code.groups_abc import CyclicGroup
333+
334+
In [2]: C = CyclicGroup(3)
335+
336+
In [3]: print(C(1) * C(2))
337+
0_C3
338+
339+
This illustrates the utility of this use of abstract base classes: the base
340+
class can specify what subclasses need to implement. If a subclass does not
341+
implement all the right attributes then a helpful error is generated, and
342+
subclasses that do implement the class fully work as expected.
220343

221344
Duck typing
222345
~~~~~~~~~~~
223346

347+
Before we turn to the second use of abstract base classes, it is useful to
348+
divert our attention to what might be thought of as the type philosophy of
349+
Python. Many programming languages are strongly typed. This means that in
350+
situations such as passing arguments to functions, the type of each variable is
351+
specified, and it is an error to pass a variable of the wrong type. This is not
352+
the Python approach. Instead, Python functions typically do not check the types
353+
of their arguments beyond ensuring that they have the basic properties required
354+
of the operation in question. It doesn't really matter what the type of an
355+
object is, so long as it has the operations, methods, and attributes required.
356+
357+
The term that is used for this approach to data types is :term:`duck typing`:
358+
if a data type walks like a duck and quacks like a duck, then it might as well
359+
be a duck. This does, however, beg the question of how a program should know if
360+
an object has the right properties in a given circumstance. It is here that the
361+
second use of abstract base classes comes into play.
362+
363+
Virtual subclasses
364+
~~~~~~~~~~~~~~~~~~
365+
366+
We learned in :numref:`Week %s <objects>` that we can determine if a type is a
367+
number by checking if it is an instance of :class:`numbers.Number`. This is a
368+
slightly different usage of abstract base classes. Rather than providing part
369+
of the implementation of, say, :class:`float`, :class:`numbers.Number` provides
370+
a categorisation of objects into numbers and non-numbers. This aids duck
371+
typing, by enabling much more general type checking.
372+
373+
The :ref:`Standard Library <library-index>` contains many abstract base classes
374+
whose role is to support duck typing by identifying objects with particular
375+
properties. For example, the :mod:`collections.abc` module provides abstract
376+
base classes for container objects with particular properties. The
377+
:class:`collections.abc.Iterable` abstract base class groups all iterable
378+
containers. For example:
379+
380+
.. code-block:: ipython3
381+
382+
In [1]: from collections.abc import Iterable
383+
384+
In [2]: from example_code.linked_list import Link
385+
386+
In [3]: issubclass(Link, Iterable)
387+
Out[3]: True
388+
389+
Hang on, though, what magic is this? We didn't declare
390+
:class:`~example_code.linked_list.Link` as inheriting from
391+
:class:`~collections.abc.Iterable`.
392+
393+
What is going on here is a form of reverse inheritance process. Rather than
394+
:class:`~example_code.linked_list.Link` declaring that it inherits from
395+
:class:`~collections.abc.Iterable`, :class:`~collections.abc.Iterable`
396+
determines that :class:`~example_code.linked_list.Link` is its subclass. It's a sort
397+
of adoption mechanism for classes. Of course the authors of the Standard
398+
Library don't know that we will declare :class:`~example_code.linked_list.Link`, so
399+
there is no code explicitly claiming :class:`~example_code.linked_list.Link` as a
400+
subclass of :class:`~collections.abc.Iterable`. Instead, *any* class which
401+
implements the :meth:`~object.__iter__` special method is a subclass of
402+
:class:`~collections.abc.Iterable`. How does this work? Well,
403+
:func:`isinstance` and :func:`issubclass` are implemented with the help of, you
404+
guessed it, yet another :term:`special method`. This time the special method is
405+
:meth:`~abc.ABCMeta.__subclasshook__`.
406+
407+
.. _subclasshook:
408+
409+
.. code-block:: python3
410+
:caption: The source code for :class:`collections.abc.Iterable` extracted
411+
from the `Git repository for the standard Python language
412+
implementation <https://github.com/python/cpython/blob/master/Lib/_collections_abc.py>`__.
413+
:linenos:
414+
415+
from abc import ABCMeta, abstractmethod
416+
417+
...
418+
419+
def _check_methods(C, *methods):
420+
mro = C.__mro__
421+
for method in methods:
422+
for B in mro:
423+
if method in B.__dict__:
424+
if B.__dict__[method] is None:
425+
return NotImplemented
426+
break
427+
else:
428+
return NotImplemented
429+
return True
430+
431+
...
432+
433+
class Iterable(metaclass=ABCMeta):
434+
435+
__slots__ = ()
436+
437+
@abstractmethod
438+
def __iter__(self):
439+
while False:
440+
yield None
441+
442+
@classmethod
443+
def __subclasshook__(cls, C):
444+
if cls is Iterable:
445+
return _check_methods(C, "__iter__")
446+
return NotImplemented
447+
448+
__class_getitem__ = classmethod(GenericAlias)
449+
450+
:numref:`subclasshook` shows the actual source code for
451+
:class:`~collections.abc.Iterable` [#python_in_python]_. Let's walk through
452+
this. The inheritance in line 19 is essentially equivalent to inheriting from
453+
:class:`abc.ABC`. Similarly, lines 21 and 34 are unrelated technical code. At
454+
line 24 we see the :meth:`object.__iter__` special method, decorated with
455+
`~abc.abstractmethod`. This ensures that classes that do explicitly inherit
456+
from :class:`~collections.abc.Iterable` have to implement
457+
:meth:`object.__iter__`.
458+
459+
The part that currently concerns us, though, is the
460+
declaration of :meth:`~abc.ABCMeta.__subclasshook__` at line 29.
461+
:meth:`~abc.ABCMeta.__subclasshook__` is declared as :term:`class method`. This
462+
means that it will be passed the class itself as its first argument, in place
463+
of the object. It is conventional to signal this difference by calling the
464+
first parameter `cls` instead of `self`. The second parameter, `C` is the class
465+
to be tested.
466+
467+
In common with the special methods for arithmetic,
468+
:meth:`~abc.ABCMeta.__subclasshook__` returns :data:`NotImplemented` to
469+
indicate cases that it cannot deal with. In this case, if the current class is
470+
not :class:`~collections.abc.Iterable` (this would happen if the method were
471+
called on a subclass of :class:`~collections.abc.Iterable`) then
472+
:data:`NotImplemented` is returned. If we really are checking `C` against
473+
:class:`~collections.abc.Iterable` then the `_check_methods` helper function is
474+
called. The fine details of how this works are a little technical, but in
475+
essence the function loops over `C` and its superclasses in order (`C.__mro__`
476+
is the :term:`method resolution order`) and checks if the relevant methods are
477+
defined. If they are all found then :data:`True` is returned, otherwise the
478+
result is :data:`NotImplemented`. An implementation of
479+
:meth:`~abc.ABCMeta.__subclasshook__` could also return :data:`False` to
480+
indicate that `C` is definitely not a subclass.
481+
224482
Glossary
225483
--------
226484

@@ -233,6 +491,16 @@ Glossary
233491
define the interfaces of :term:`methods <method>` but leave their implementations
234492
to the concrete :term:`child classes <child class>`.
235493

494+
abstract method
495+
A method whose presence is required by an :term:`abstract base class`
496+
but for which concrete subclasses are required to provide the implementation.
497+
498+
class method
499+
A :term:`method` which belongs to the class itself, rather than to the
500+
instances of the class. Class methods are declared using the
501+
`classmethod` :term:`decorator` and take the class (`cls`) as their first
502+
argument, instead of the instance (`self`). See also: :term:`class attribute`.
503+
236504
decorator
237505
A syntax for applying :term:`higher order functions <higher order
238506
function>` when defining functions. A decorator is applied by writing
@@ -250,12 +518,76 @@ Glossary
250518
A function which acts on other functions, and which possibly returns
251519
another function as its result.
252520

521+
method resolution order
522+
MRO
523+
A sequence of the superclasses of the current class ordered by
524+
increasing ancestry. The MRO is searched in order to find
525+
implementations of :term:`methods <method>` and :term:`attributes
526+
<attribute>`.
527+
253528
syntactic sugar
254529
A feature of the programming language which adds no new functionality,
255530
but which enables a clearer or more concise syntax. Python
256531
:term:`special methods <special method>` are a form of syntactic sugar as they enable,
257532
for example, the syntax `a + b` instead of something like `a.add(b)`.
258533

534+
virtual subclass
535+
A class which does not declare its descent from the superclass through
536+
its definition, but which is instead claimed as a subclass by the
537+
superclass.
538+
539+
540+
Exercises
541+
---------
259542

260543
Exam preparation
261-
----------------
544+
----------------
545+
546+
The exam will be similar in format to the :ref:`midterm test <midterm>`, so all
547+
of the advice about preparing applies there too. As with all second year
548+
elective modules, the exam will comprise four questions, each marked out of 20.
549+
550+
As with everything in this course, the one thing you can do to effectively
551+
prepare for the exam is to program. You should complete any of the exercises in
552+
the course that you have not yet done, and more exercises are given below.
553+
554+
Exam scope
555+
~~~~~~~~~~
556+
557+
Everything we have covered in the course up to and including week 10 will be
558+
fully examinable. The week 11 material is not examinable with the
559+
following exceptions:
560+
561+
1. You may need to use :term:`abstract base classes <abstract base class>` from
562+
the standard library to check the type of variables. This is simply what you
563+
have been doing all term, for example using :class:`numbers.Number` to check
564+
that a value is numeric.
565+
2. The skeleton code may include :term:`abstract base classes <abstract base
566+
class>` from which your classes may need to inherit. This is actually a help
567+
to you in the exam, because the :term:`abstract methods <abstract method>`
568+
will provide information about what you need to implement, and a helpful
569+
error message if you haven't done so.
570+
571+
Support while revising
572+
~~~~~~~~~~~~~~~~~~~~~~
573+
574+
The module Piazza forum will remain open throughout the revision period and we
575+
will be very happy to respond to your questions. There will also be a live
576+
revision session during week 1 of summer term in the module team. This will be
577+
an opportunity to ask individual questions just like in the labs. If enough
578+
people attend then I will also run a group Q & A session.
579+
580+
Practice questions
581+
~~~~~~~~~~~~~~~~~~
582+
583+
Some specifically-designed practice questions will be released at about the end
584+
of term. In addition to this, there are a lot of very good exercises in
585+
chapters 7 and 9 of `Hans Petter Langtangen, A Primer on Scientific Programming
586+
with Python <https://link.springer.com/book/10.1007%2F978-3-662-49887-3>`__.
587+
You can access that book by logging in with your Imperial credentials.
588+
589+
.. rubric:: Footnotes
590+
591+
.. [#python_in_python] Most of the :ref:`Python Standard Library <library-index>` is written
592+
in Python, so diving in and reading the source code is often an option if
593+
you really want to know how some part of the language works.

0 commit comments

Comments
 (0)