@@ -5,6 +5,8 @@ This week we'll tie up a few loose ends by examining in detail some programming
55concepts and Python features which we have encountered but not really studied
66in the course so far.
77
8+ .. _decorators :
9+
810Decorators
911----------
1012
@@ -94,11 +96,11 @@ docstring for the original function, and not that of the decorator.
9496Decorators 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
99101function name as an argument. How does this work? The answer is yet another
100102wrapper 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
102104a function but other decorators take other types) and returns a decorator
103105function. That is, it is a function which takes in arguments and returns a
104106function 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
217219abstract 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
221344Duck 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+
224482Glossary
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
260543Exam 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