@@ -5,6 +5,8 @@ This week we'll tie up a few loose ends by examining in detail some programming
5
5
concepts and Python features which we have encountered but not really studied
6
6
in the course so far.
7
7
8
+ .. _decorators :
9
+
8
10
Decorators
9
11
----------
10
12
@@ -94,11 +96,11 @@ docstring for the original function, and not that of the decorator.
94
96
Decorators which take arguments
95
97
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
96
98
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
99
101
function name as an argument. How does this work? The answer is yet another
100
102
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
102
104
a function but other decorators take other types) and returns a decorator
103
105
function. That is, it is a function which takes in arguments and returns a
104
106
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
217
219
abstract base classes easy. In Python, these features are provided by the
218
220
:mod: `abc ` module in the :ref: `Standard Library <library-index >`.
219
221
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.
220
343
221
344
Duck typing
222
345
~~~~~~~~~~~
223
346
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
+
224
482
Glossary
225
483
--------
226
484
@@ -233,6 +491,16 @@ Glossary
233
491
define the interfaces of :term: `methods <method> ` but leave their implementations
234
492
to the concrete :term: `child classes <child class> `.
235
493
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
+
236
504
decorator
237
505
A syntax for applying :term: `higher order functions <higher order
238
506
function> ` when defining functions. A decorator is applied by writing
@@ -250,12 +518,76 @@ Glossary
250
518
A function which acts on other functions, and which possibly returns
251
519
another function as its result.
252
520
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
+
253
528
syntactic sugar
254
529
A feature of the programming language which adds no new functionality,
255
530
but which enables a clearer or more concise syntax. Python
256
531
:term: `special methods <special method> ` are a form of syntactic sugar as they enable,
257
532
for example, the syntax `a + b ` instead of something like `a.add(b) `.
258
533
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
+ ---------
259
542
260
543
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