-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy path10_further_object-oriented_features.html
More file actions
611 lines (584 loc) · 75.5 KB
/
10_further_object-oriented_features.html
File metadata and controls
611 lines (584 loc) · 75.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>10. Further object-oriented features — Object-oriented Programming documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=03e43079" />
<link rel="stylesheet" type="text/css" href="_static/fenics.css?v=8c7d05f9" />
<link rel="stylesheet" type="text/css" href="_static/proof.css" />
<link rel="stylesheet" type="text/css" href="_static/graphviz.css?v=fd3f3429" />
<script src="_static/documentation_options.js?v=5929fcd5"></script>
<script src="_static/doctools.js?v=9a2dae69"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/proof.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="1. Getting help" href="a1_help.html" />
<link rel="prev" title="9. Trees and directed acyclic graphs" href="9_trees_and_directed_acyclic_graphs.html" />
<!--[if lte IE 6]>
<link rel="stylesheet" href="_static/ie6.css" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0EFVH5C4DC"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-0EFVH5C4DC');
</script>
<link rel="stylesheet" href="_static/featured.css">
<link rel="shortcut icon" href="_static/icon.ico" />
</head><body>
<div class="wrapper">
<a href="index.html"><img src="_static/banner.png" width="900px" alt="FInAT Project Banner" /></a>
<div id="access">
<div class="menu">
<ul>
<li class="page_item"><a href="index.html" title="Book">Book</a></li>
<li class="page_item"><a href="videos.html" title="Videos">Videos</a></li>
<li class="page_item"><a href="exercises.html"
title="Exercises">Exercises</a></li>
<li class="page_item"><a href="installation.html" title="Installation">Installation</a></li>
</ul>
</div><!-- .menu -->
</div><!-- #access -->
</div><!-- #wrapper -->
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="further-object-oriented-features">
<h1><span class="section-number">10. </span>Further object-oriented features<a class="headerlink" href="#further-object-oriented-features" title="Link to this heading">¶</a></h1>
<p>In this chapter, we’ll tie up a few loose ends by examining in detail some
programming concepts and Python features which we have encountered but not
really studied in the preceding chapters.</p>
<section id="decorators">
<span id="id1"></span><h2><span class="section-number">10.1. </span>Decorators<a class="headerlink" href="#decorators" title="Link to this heading">¶</a></h2>
<details>
<summary>
Video: decorators.</summary><div class="video_wrapper" style="">
<iframe allowfullscreen="true" src="https://player.vimeo.com/video/526946976" style="border: 0; height: 345px; width: 560px">
</iframe></div><p>Imperial students can also <a class="reference external" href="https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=75d2f2a5-5ab9-464b-8eee-ae1c00dbf366">watch this video on Panopto</a>.</p>
</details><p>In <a class="reference internal" href="9_trees_and_directed_acyclic_graphs.html#trees"><span class="std std-numref">Chapter 9</span></a> we encountered the
<a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.singledispatch" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.singledispatch()</span></code></a> decorator, which turns a function into a
<a class="reference internal" href="9_trees_and_directed_acyclic_graphs.html#term-single-dispatch-function"><span class="xref std std-term">single dispatch function</span></a>. More generally, a decorator is a function
which takes in a function and returns another function. In other words, the
following:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="nd">@dec</span>
<span class="k">def</span><span class="w"> </span><span class="nf">func</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</div>
<p>is equivalent to:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">func</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="o">...</span>
<span class="n">func</span> <span class="o">=</span> <span class="n">dec</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
</pre></div>
</div>
<p>Decorators are therefore merely <a class="reference internal" href="#term-syntactic-sugar"><span class="xref std std-term">syntactic sugar</span></a>, but can be very useful
in removing the need for boiler-plate code at the top of functions. For
example, your code for <a class="reference internal" href="9_trees_and_directed_acyclic_graphs.html#ex-expr"><span class="std std-numref">Exercise 9.6</span></a> probably contains a lot
of repeated code a similar to the following:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="fm">__add__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""Return the Expr for the sum of this Expr and another."""</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">numbers</span><span class="o">.</span><span class="n">Number</span><span class="p">):</span>
<span class="n">other</span> <span class="o">=</span> <span class="n">Number</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="k">return</span> <span class="n">Add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span>
</pre></div>
</div>
<p>We could define a decorator to clean up this code as follows:</p>
<div class="literal-block-wrapper docutils container" id="id5">
<span id="eg-decorator"></span><div class="code-block-caption"><span class="caption-number">Listing 10.1 </span><span class="caption-text">A <a class="reference internal" href="#term-decorator"><span class="xref std std-term">decorator</span></a> which casts the second argument of a method
to an <code class="xref py py-obj docutils literal notranslate"><span class="pre">expressions.Number</span></code> if that argument is a number.</span><a class="headerlink" href="#id5" title="Link to this code">¶</a></div>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="kn">from</span><span class="w"> </span><span class="nn">functools</span><span class="w"> </span><span class="kn">import</span> <span class="n">wraps</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">def</span><span class="w"> </span><span class="nf">make_other_expr</span><span class="p">(</span><span class="n">meth</span><span class="p">):</span>
<span class="linenos"> 4</span><span class="w"> </span><span class="sd">"""Cast the second argument of a method to Number when needed."""</span>
<span class="linenos"> 5</span> <span class="nd">@wraps</span><span class="p">(</span><span class="n">meth</span><span class="p">)</span>
<span class="linenos"> 6</span> <span class="k">def</span><span class="w"> </span><span class="nf">fn</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="linenos"> 7</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">numbers</span><span class="o">.</span><span class="n">Number</span><span class="p">):</span>
<span class="linenos"> 8</span> <span class="n">other</span> <span class="o">=</span> <span class="n">Number</span><span class="p">(</span><span class="n">other</span><span class="p">)</span>
<span class="linenos"> 9</span> <span class="k">return</span> <span class="n">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span>
<span class="linenos">10</span> <span class="k">return</span> <span class="n">fn</span>
</pre></div>
</div>
</div>
<p>Now, each time we write one of the special methods of <code class="xref py py-class docutils literal notranslate"><span class="pre">Expr</span></code>, we can
instead write something like the following:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="nd">@make_other_expr</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__add__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
<span class="w"> </span><span class="sd">"""Return the Expr for the sum of this Expr and another."""</span>
<span class="k">return</span> <span class="n">Add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">)</span>
</pre></div>
</div>
<p>Let’s look closely at what the decorator in <a class="reference internal" href="#eg-decorator"><span class="std std-numref">Listing 10.1</span></a> does. The
decorator takes in one function, <code class="xref py py-func docutils literal notranslate"><span class="pre">meth()</span></code> an returns another one,
<code class="xref py py-func docutils literal notranslate"><span class="pre">fn()</span></code>. Notice that we let <code class="xref py py-func docutils literal notranslate"><span class="pre">fn()</span></code> take the same arguments as
<code class="xref py py-func docutils literal notranslate"><span class="pre">meth()</span></code>. If you wanted to write a more generic decorator that worked on
functions with different signatures, then you could define function as
<code class="xref py py-obj docutils literal notranslate"><span class="pre">fn(*args,</span> <span class="pre">**kwargs)</span></code> and pass these through to <code class="xref py py-func docutils literal notranslate"><span class="pre">meth()</span></code>.</p>
<p>The contents of <code class="xref py py-func docutils literal notranslate"><span class="pre">fn()</span></code> are what will be executed every time <code class="xref py py-func docutils literal notranslate"><span class="pre">meth()</span></code> is
called. We use this to check the type of <code class="xref py py-data docutils literal notranslate"><span class="pre">other</span></code> and cast it to
<code class="xref py py-class docutils literal notranslate"><span class="pre">Number</span></code>, and then call the original <code class="xref py py-func docutils literal notranslate"><span class="pre">meth()</span></code> on the modified arguments.
We could also execute code that acts on the value that <code class="xref py py-func docutils literal notranslate"><span class="pre">meth()</span></code> returns. To
do this we would assign the result of <code class="xref py py-func docutils literal notranslate"><span class="pre">meth()</span></code> to a variable and then
include more code after line 9.</p>
<p>Finally, notice that we have wrapped <code class="xref py py-obj docutils literal notranslate"><span class="pre">fn</span></code> in another decorator,
<a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.wraps" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.wraps()</span></code></a>. The purpose of this decorator is to copy the name and
docstring from <code class="xref py py-func docutils literal notranslate"><span class="pre">meth()</span></code> to <code class="xref py py-func docutils literal notranslate"><span class="pre">fn()</span></code>. The effect of this is that if the
user calls <a class="reference external" href="https://docs.python.org/3/library/functions.html#help" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">help()</span></code></a> on a decorated function then they will see the name and
docstring for the original function, and not that of the decorator.</p>
<section id="decorators-which-take-arguments">
<h3><span class="section-number">10.1.1. </span>Decorators which take arguments<a class="headerlink" href="#decorators-which-take-arguments" title="Link to this heading">¶</a></h3>
<p>Our <code class="xref py py-obj docutils literal notranslate"><span class="pre">make_other_expr</span></code> decorator doesn’t have brackets after its name, and doesn’t
take any arguments. However <a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.wraps" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.wraps()</span></code></a> does have brackets, and takes a
function name as an argument. How does this work? The answer is yet another
wrapper function. A decorator is a function which takes a function and
returns a function. <a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.wraps" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">functools.wraps()</span></code></a> takes an argument (it happens to be
a function but other decorators take other types) and returns a decorator
function. That is, it is a function which takes in arguments and returns a
function which takes a function and returns a function. It’s functions all the
way down!</p>
</section>
<section id="the-property-decorator">
<h3><span class="section-number">10.1.2. </span>The property decorator<a class="headerlink" href="#the-property-decorator" title="Link to this heading">¶</a></h3>
<p>Back in <a class="reference internal" href="3_objects.html#objects"><span class="std std-numref">Chapter 3</span></a>, we gave the
<a class="reference internal" href="example_code.html#example_code.polynomial.Polynomial" title="example_code.polynomial.Polynomial"><code class="xref py py-class docutils literal notranslate"><span class="pre">Polynomial</span></code></a> class a
<a class="reference internal" href="example_code.html#example_code.polynomial.Polynomial.degree" title="example_code.polynomial.Polynomial.degree"><code class="xref py py-meth docutils literal notranslate"><span class="pre">degree()</span></code></a> method:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span><span class="w"> </span><span class="nf">degree</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">coefficients</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
</pre></div>
</div>
<p>This enables the following code to work:</p>
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="gp">In [1]: </span><span class="kn">from</span><span class="w"> </span><span class="nn">example_code.polynomial</span><span class="w"> </span><span class="kn">import</span> <span class="n">Polynomial</span>
<span class="gp">In [2]: </span><span class="n">p</span> <span class="o">=</span> <span class="n">Polynomial</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="gp">In [3]: </span><span class="n">p</span><span class="o">.</span><span class="n">degree</span><span class="p">()</span>
<span class="gh">Out[3]: </span><span class="go">2</span>
</pre></div>
</div>
<p>However, the empty brackets at the end of <code class="xref py py-func docutils literal notranslate"><span class="pre">degree()</span></code> are a bit clunky: why
should we have to provide empty brackets if there are no arguments to pass?
This represents a failure of <a class="reference internal" href="3_objects.html#term-encapsulation"><span class="xref std std-term">encapsulation</span></a>, because we
shouldn’t know or care from the outside whether
<a class="reference internal" href="example_code.html#example_code.polynomial.Polynomial.degree" title="example_code.polynomial.Polynomial.degree"><code class="xref py py-meth docutils literal notranslate"><span class="pre">degree()</span></code></a> is a <a class="reference internal" href="3_objects.html#term-method"><span class="xref std std-term">method</span></a> or a
<a class="reference internal" href="3_objects.html#term-data-attribute"><span class="xref std std-term">data attribute</span></a>. Indeed, the developer of the
<a class="reference internal" href="example_code.html#module-example_code.polynomial" title="example_code.polynomial"><code class="xref py py-mod docutils literal notranslate"><span class="pre">polynomial</span></code></a> module should be able to change that
implementation without changing the interface. This is where the
built-in <a class="reference external" href="https://docs.python.org/3/library/functions.html#property" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">property</span></code></a> decorator comes in. <a class="reference external" href="https://docs.python.org/3/library/functions.html#property" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">property</span></code></a> transforms
methods that take no arguments other than the object itself into attributes.
So, if we had instead defined:</p>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="nd">@property</span>
<span class="k">def</span><span class="w"> </span><span class="nf">degree</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">coefficients</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span>
</pre></div>
</div>
<p>Then <code class="xref py py-obj docutils literal notranslate"><span class="pre">degree</span></code> would be accessible as an <a class="reference internal" href="3_objects.html#term-attribute"><span class="xref std std-term">attribute</span></a>:</p>
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="gp">In [1]: </span><span class="kn">from</span><span class="w"> </span><span class="nn">example_code.polynomial</span><span class="w"> </span><span class="kn">import</span> <span class="n">Polynomial</span>
<span class="gp">In [2]: </span><span class="n">p</span> <span class="o">=</span> <span class="n">Polynomial</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="gp">In [3]: </span><span class="n">p</span><span class="o">.</span><span class="n">degree</span>
<span class="gh">Out[3]: </span><span class="go">2</span>
</pre></div>
</div>
</section>
<section id="the-functools-module">
<h3><span class="section-number">10.1.3. </span>The <a class="reference external" href="https://docs.python.org/3/library/functools.html#module-functools" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">functools</span></code></a> module<a class="headerlink" href="#the-functools-module" title="Link to this heading">¶</a></h3>
<p>The <a class="reference external" href="https://docs.python.org/3/library/functools.html#module-functools" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">functools</span></code></a> module is part of the <a class="reference external" href="https://docs.python.org/3/library/index.html#library-index" title="(in Python v3.14)"><span class="xref std std-ref">Python Standard Library</span></a>. It provides a collection of core <a class="reference internal" href="#term-higher-order-function"><span class="xref std std-term">higher order
functions</span></a>, some of which we have already met earlier
in the course. Since decorators are an important class of higher order
function, it is unsurprising that <a class="reference external" href="https://docs.python.org/3/library/functools.html#module-functools" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">functools</span></code></a> provides several very useful
ones. We will survey just a few here:</p>
<dl class="simple">
<dt><a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.cache" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">functools.cache</span></code></a></dt><dd><p>Some functions can be very expensive to compute, and may be called
repeatedly. A cache stores the results of previous function calls. If the
function is called again with a combination of argument values that have
previously been used, the function result is returned from the cache
instead of the function being called again. This is a trade-off of
execution time against memory usage, so one has to be careful how much
memory will be consumed by the cache.</p>
</dd>
<dt><a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.lru_cache" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">functools.lru_cache</span></code></a></dt><dd><p>A least recently used cache is a limited size cache where the least
recently accessed items will be discarded if the cache is full. This has
the advantage that the memory usage is bounded, but the drawback that cache
eviction may take time, and that more recomputation may occur than in an
unbounded cache.</p>
</dd>
<dt><a class="reference external" href="https://docs.python.org/3/library/functools.html#functools.singledispatch" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">functools.singledispatch</span></code></a></dt><dd><p>We met this in <a class="reference internal" href="9_trees_and_directed_acyclic_graphs.html#single-dispatch"><span class="std std-numref">Section 9.4.3</span></a>. This decorator transforms a
function into a <a class="reference internal" href="9_trees_and_directed_acyclic_graphs.html#term-single-dispatch-function"><span class="xref std std-term">single dispatch function</span></a>.</p>
</dd>
</dl>
</section>
</section>
<section id="abstract-base-classes">
<span id="id2"></span><h2><span class="section-number">10.2. </span>Abstract base classes<a class="headerlink" href="#abstract-base-classes" title="Link to this heading">¶</a></h2>
<details>
<summary>
Video: Abstract base classes.</summary><div class="video_wrapper" style="">
<iframe allowfullscreen="true" src="https://player.vimeo.com/video/526947635" style="border: 0; height: 345px; width: 560px">
</iframe></div><p>Imperial students can also <a class="reference external" href="https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=9a9b872a-9499-40b7-a228-ae1c00dbf947">watch this video on Panopto</a>.</p>
</details><p>We have now on several occasions encountered classes which are not designed to
be instantiated themselves, but merely serve as parent classes to concrete
classes which are intended to be instantiated. Examples of these classes
include <a class="reference external" href="https://docs.python.org/3/library/numbers.html#numbers.Number" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">numbers.Number</span></code></a>, <a class="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><code class="xref py py-class docutils literal notranslate"><span class="pre">example_code.groups.Group</span></code></a>, and the
<code class="xref py py-class docutils literal notranslate"><span class="pre">Expr</span></code>, <code class="xref py py-class docutils literal notranslate"><span class="pre">Operator</span></code>, and <code class="xref py py-class docutils literal notranslate"><span class="pre">Terminal</span></code> classes from
<a class="reference internal" href="9_trees_and_directed_acyclic_graphs.html#trees"><span class="std std-numref">Chapter 9</span></a>. These classes that are only ever parents are
called <a class="reference internal" href="#term-abstract-base-class"><span class="xref std std-term">abstract base classes</span></a>. They are abstract
in the sense that they define (some of the) properties of their children, but
without providing full implementations of them. They are base classes in the
sense that they are intended to be inherited from.</p>
<p>Abstract base classes typically fulfil two related roles: they provide the
definition of an interface that child classes can be expected to follow, and
they provide a useful way of checking that an object of a concrete class has
particular properties.</p>
<section id="the-abc-module">
<h3><span class="section-number">10.2.1. </span>The <a class="reference external" href="https://docs.python.org/3/library/abc.html#module-abc" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">abc</span></code></a> module<a class="headerlink" href="#the-abc-module" title="Link to this heading">¶</a></h3>
<p>The concept of an abstract base class is itself an abstraction: an
abstract base class is simply a class which is designed not to be instantiated.
This requires no support from particular language features. Nonetheless, there
are features that a language can provide which makes the creation of useful
abstract base classes easy. In Python, these features are provided by the
<a class="reference external" href="https://docs.python.org/3/library/abc.html#module-abc" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">abc</span></code></a> module in the <a class="reference external" href="https://docs.python.org/3/library/index.html#library-index" title="(in Python v3.14)"><span class="xref std std-ref">Standard Library</span></a>.</p>
<p>The <a class="reference external" href="https://docs.python.org/3/library/abc.html#module-abc" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">abc</span></code></a> module provides the <a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABC" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></a> class. This is itself an
abstract base class: there is no need to ever make an object of type
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABC" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></a>. Instead, classes inherit from <a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABC" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></a> in order to
access features that it provides.</p>
</section>
<section id="abstract-methods">
<h3><span class="section-number">10.2.2. </span>Abstract methods<a class="headerlink" href="#abstract-methods" title="Link to this heading">¶</a></h3>
<p>Let’s look back at the groups example from <a class="reference internal" href="7_inheritance.html#inheritance"><span class="std std-numref">Chapter 7</span></a>. We
defined the base <a class="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><code class="xref py py-class docutils literal notranslate"><span class="pre">Group</span></code></a> class and specified that
child classes had to implement the <code class="xref py py-meth docutils literal notranslate"><span class="pre">_validate()</span></code> and <code class="xref py py-meth docutils literal notranslate"><span class="pre">operator()</span></code>
methods as well as the <code class="xref py py-attr docutils literal notranslate"><span class="pre">symbol</span></code> <a class="reference internal" href="7_inheritance.html#term-class-attribute"><span class="xref std std-term">class attribute</span></a>. But how should
we actually know that these methods and attribute are required? This might be
documented, but that is somewhat hit and miss: it is often less than
completely obvious where to look for the documentation. Abstract methods
provide a much more satisfactory solution to this problem. The
<a class="reference internal" href="example_code.html#module-example_code.groups_abc" title="example_code.groups_abc"><code class="xref py py-mod docutils literal notranslate"><span class="pre">example_code.groups_abc</span></code></a> module is an update of the
<a class="reference internal" href="example_code.html#module-example_code.groups" title="example_code.groups"><code class="xref py py-mod docutils literal notranslate"><span class="pre">example_code.groups</span></code></a> module which uses the <a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABC" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></a> class.</p>
<div class="literal-block-wrapper docutils container" id="id6">
<span id="groups-abc"></span><div class="code-block-caption"><span class="caption-number">Listing 10.2 </span><span class="caption-text">An abstract base class version of the
<a class="reference internal" href="example_code.html#example_code.groups_abc.Group" title="example_code.groups_abc.Group"><code class="xref py py-class docutils literal notranslate"><span class="pre">Group</span></code></a> class. Note that the class itself
inherits from <a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABC" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></a>, and the methods and attribute to be
implemented by the <a class="reference internal" href="7_inheritance.html#term-child-class"><span class="xref std std-term">child classes</span></a> have the
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.abstractmethod" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">abstractmethod</span></code></a> decorator.</span><a class="headerlink" href="#id6" title="Link to this code">¶</a></div>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="kn">from</span><span class="w"> </span><span class="nn">abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">ABC</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">class</span><span class="w"> </span><span class="nc">Group</span><span class="p">(</span><span class="n">ABC</span><span class="p">):</span>
<span class="linenos"> 4</span><span class="w"> </span><span class="sd">"""A base class containing methods common to many groups.</span>
<span class="linenos"> 5</span>
<span class="linenos"> 6</span><span class="sd"> Each subclass represents a family of parametrised groups.</span>
<span class="linenos"> 7</span>
<span class="linenos"> 8</span><span class="sd"> Parameters</span>
<span class="linenos"> 9</span><span class="sd"> ----------</span>
<span class="linenos">10</span><span class="sd"> n: int</span>
<span class="linenos">11</span><span class="sd"> The primary group parameter, such as order or degree. The</span>
<span class="linenos">12</span><span class="sd"> precise meaning of n changes from subclass to subclass.</span>
<span class="linenos">13</span><span class="sd"> """</span>
<span class="linenos">14</span>
<span class="linenos">15</span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
<span class="linenos">16</span> <span class="bp">self</span><span class="o">.</span><span class="n">n</span> <span class="o">=</span> <span class="n">n</span>
<span class="linenos">17</span>
<span class="linenos">18</span> <span class="nd">@property</span>
<span class="linenos">19</span> <span class="nd">@abstractmethod</span>
<span class="linenos">20</span> <span class="k">def</span><span class="w"> </span><span class="nf">symbol</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="linenos">21</span><span class="w"> </span><span class="sd">"""Represent the group name as a character."""</span>
<span class="linenos">22</span> <span class="k">pass</span>
<span class="linenos">23</span>
<span class="linenos">24</span> <span class="nd">@abstractmethod</span>
<span class="linenos">25</span> <span class="k">def</span><span class="w"> </span><span class="nf">_validate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="linenos">26</span><span class="w"> </span><span class="sd">"""Ensure that value is an allowed element value in this group."""</span>
<span class="linenos">27</span> <span class="k">pass</span>
<span class="linenos">28</span>
<span class="linenos">29</span> <span class="nd">@abstractmethod</span>
<span class="linenos">30</span> <span class="k">def</span><span class="w"> </span><span class="nf">operation</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="linenos">31</span><span class="w"> </span><span class="sd">"""Return a ∘ b using the group operation ∘."""</span>
<span class="linenos">32</span> <span class="k">pass</span>
<span class="linenos">33</span>
<span class="linenos">34</span> <span class="k">def</span><span class="w"> </span><span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="linenos">35</span><span class="w"> </span><span class="sd">"""Create an element of this group."""</span>
<span class="linenos">36</span> <span class="k">return</span> <span class="n">Element</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="linenos">37</span>
<span class="linenos">38</span> <span class="k">def</span><span class="w"> </span><span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="linenos">39</span><span class="w"> </span><span class="sd">"""Return a string in the form symbol then group parameter."""</span>
<span class="linenos">40</span> <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">symbol</span><span class="si">}{</span><span class="bp">self</span><span class="o">.</span><span class="n">n</span><span class="si">}</span><span class="s2">"</span>
<span class="linenos">41</span>
<span class="linenos">42</span> <span class="k">def</span><span class="w"> </span><span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="linenos">43</span><span class="w"> </span><span class="sd">"""Return the canonical string representation of the element."""</span>
<span class="linenos">44</span> <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="nb">type</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">(</span><span class="si">{</span><span class="nb">repr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">n</span><span class="p">)</span><span class="si">}</span><span class="s2">)"</span>
</pre></div>
</div>
</div>
<p>There are a few features of <a class="reference internal" href="#groups-abc"><span class="std std-numref">Listing 10.2</span></a> which are noteworthy. First,
observe that <a class="reference internal" href="example_code.html#example_code.groups_abc.Group" title="example_code.groups_abc.Group"><code class="xref py py-class docutils literal notranslate"><span class="pre">Group</span></code></a> now inherits from
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABC" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></a>. This simply enables the features that we will use next.
The new <a class="reference internal" href="example_code.html#example_code.groups_abc.Group" title="example_code.groups_abc.Group"><code class="xref py py-class docutils literal notranslate"><span class="pre">Group</span></code></a> class has
<code class="xref py py-meth docutils literal notranslate"><span class="pre">_validate()</span></code> and
<code class="xref py py-meth docutils literal notranslate"><span class="pre">operator()</span></code> methods, but these don’t
actually do anything (their contents are merely <a class="reference external" href="https://docs.python.org/3/reference/simple_stmts.html#pass" title="(in Python v3.14)"><code class="xref std std-keyword docutils literal notranslate"><span class="pre">pass</span></code></a>). They are,
however, decorated with <a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.abstractmethod" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">abstractmethod</span></code></a>. The effect of this decorator can
be observed if we try to instantiate <a class="reference internal" href="example_code.html#example_code.groups_abc.Group" title="example_code.groups_abc.Group"><code class="xref py py-class docutils literal notranslate"><span class="pre">Group</span></code></a>:</p>
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="gp">In [1]: </span><span class="kn">from</span><span class="w"> </span><span class="nn">example_code.groups_abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">Group</span>
<span class="gp">In [2]: </span><span class="n">Group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="gt">--------------------------------------------------------------------------</span>
<span class="ne">TypeError</span><span class="g g-Whitespace"> </span>Traceback (most recent call last)
<span class="n">Cell</span> <span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">line</span> <span class="mi">1</span>
<span class="ne">----> </span><span class="mi">1</span> <span class="n">Group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="ne">TypeError</span>: Can't instantiate abstract class Group with abstract methods _validate, operation, symbol
</pre></div>
</div>
<p>The combination of inheriting from <a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABC" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">ABC</span></code></a> and the
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.abstractmethod" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">abstractmethod</span></code></a> decorator has the effect that instantiating this class is
an error, and we are told why. We have skipped over the <code class="xref py py-obj docutils literal notranslate"><span class="pre">symbol</span></code> attribute.
There is no <code class="xref py py-obj docutils literal notranslate"><span class="pre">abstractattribute</span></code> decorator, but the same effect can be achieved
by creating an <a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.abstractmethod" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">abstractmethod</span></code></a> and converting it into a data attribute using
<a class="reference external" href="https://docs.python.org/3/library/functions.html#property" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">property</span></code></a>. In this case, the order of decorators is important:
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.abstractmethod" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">abstractmethod</span></code></a> always needs to be the last, innermost, decorator.</p>
<p>The subclasses of <a class="reference internal" href="example_code.html#example_code.groups_abc.Group" title="example_code.groups_abc.Group"><code class="xref py py-class docutils literal notranslate"><span class="pre">Group</span></code></a> that we defined,
define all three of these attributes, so they can still be instantiated. For
example:</p>
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="gp">In [1]: </span><span class="kn">from</span><span class="w"> </span><span class="nn">example_code.groups_abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">CyclicGroup</span>
<span class="gp">In [2]: </span><span class="n">C</span> <span class="o">=</span> <span class="n">CyclicGroup</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="gp">In [3]: </span><span class="nb">print</span><span class="p">(</span><span class="n">C</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">C</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span>
<span class="go">0_C3</span>
</pre></div>
</div>
<p>This illustrates the utility of this use of abstract base classes: the base
class can specify what subclasses need to implement. If a subclass does not
implement all the right attributes then a helpful error is generated, and
subclasses that do implement the class fully work as expected.</p>
</section>
<section id="duck-typing">
<h3><span class="section-number">10.2.3. </span>Duck typing<a class="headerlink" href="#duck-typing" title="Link to this heading">¶</a></h3>
<p>Before we turn to the second use of abstract base classes, it is useful to
divert our attention to what might be thought of as the type philosophy of
Python. Many programming languages are strongly typed. This means that in
situations such as passing arguments to functions, the type of each variable is
specified, and it is an error to pass a variable of the wrong type. This is not
the Python approach. Instead, Python functions typically do not check the types
of their arguments beyond ensuring that they have the basic properties required
of the operation in question. It doesn’t really matter what the type of an
object is, so long as it has the operations, methods, and attributes required.</p>
<p>The term that is used for this approach to data types is <a class="reference internal" href="#term-duck-typing"><span class="xref std std-term">duck typing</span></a>:
if a data type walks like a duck and quacks like a duck, then it might as well
be a duck. This does, however, beg the question of how a program should know if
an object has the right properties in a given circumstance. It is here that the
second use of abstract base classes comes into play.</p>
</section>
<section id="virtual-subclasses">
<h3><span class="section-number">10.2.4. </span>Virtual subclasses<a class="headerlink" href="#virtual-subclasses" title="Link to this heading">¶</a></h3>
<details>
<summary>
Video: virtual subclasses.</summary><div class="video_wrapper" style="">
<iframe allowfullscreen="true" src="https://player.vimeo.com/video/526947427" style="border: 0; height: 345px; width: 560px">
</iframe></div><p>Imperial students can also <a class="reference external" href="https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=fd48b1c1-8e81-4c6d-8415-ae1c00dc07eb">watch this video on Panopto</a>.</p>
</details><p>We learned in <a class="reference internal" href="3_objects.html#objects"><span class="std std-numref">Chapter 3</span></a> that we can determine if a type is a
number by checking if it is an instance of <a class="reference external" href="https://docs.python.org/3/library/numbers.html#numbers.Number" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">numbers.Number</span></code></a>. This is a
slightly different usage of abstract base classes. Rather than providing part
of the implementation of types such as <a class="reference external" href="https://docs.python.org/3/library/functions.html#float" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">float</span></code></a>, <a class="reference external" href="https://docs.python.org/3/library/numbers.html#numbers.Number" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">numbers.Number</span></code></a> provides
a categorisation of objects into numbers and non-numbers. This aids duck
typing, by enabling much more general type checking.</p>
<p>The <a class="reference external" href="https://docs.python.org/3/library/index.html#library-index" title="(in Python v3.14)"><span class="xref std std-ref">Standard Library</span></a> contains many abstract base classes
whose role is to support duck typing by identifying objects with particular
properties. For example, the <a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#module-collections.abc" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">collections.abc</span></code></a> module provides abstract
base classes for container objects with particular properties. The
<a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">collections.abc.Iterable</span></code></a> abstract base class groups all iterable
containers. For example:</p>
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="gp">In [1]: </span><span class="kn">from</span><span class="w"> </span><span class="nn">collections.abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">Iterable</span>
<span class="gp">In [2]: </span><span class="kn">from</span><span class="w"> </span><span class="nn">example_code.linked_list</span><span class="w"> </span><span class="kn">import</span> <span class="n">Link</span>
<span class="gp">In [3]: </span><span class="nb">issubclass</span><span class="p">(</span><span class="n">Link</span><span class="p">,</span> <span class="n">Iterable</span><span class="p">)</span>
<span class="gh">Out[3]: </span><span class="go">True</span>
</pre></div>
</div>
<p>Hang on, though, what magic is this? We didn’t declare
<a class="reference internal" href="example_code.html#example_code.linked_list.Link" title="example_code.linked_list.Link"><code class="xref py py-class docutils literal notranslate"><span class="pre">Link</span></code></a> as inheriting from
<a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a>.</p>
<p>What is going on here is a form of reverse inheritance process. Rather than
<a class="reference internal" href="example_code.html#example_code.linked_list.Link" title="example_code.linked_list.Link"><code class="xref py py-class docutils literal notranslate"><span class="pre">Link</span></code></a> declaring that it inherits from
<a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a>, <a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a>
determines that <a class="reference internal" href="example_code.html#example_code.linked_list.Link" title="example_code.linked_list.Link"><code class="xref py py-class docutils literal notranslate"><span class="pre">Link</span></code></a> is its subclass. It’s a sort
of adoption mechanism for classes. Of course the authors of the Standard
Library don’t know that we will declare <a class="reference internal" href="example_code.html#example_code.linked_list.Link" title="example_code.linked_list.Link"><code class="xref py py-class docutils literal notranslate"><span class="pre">Link</span></code></a>, so
there is no code explicitly claiming <a class="reference internal" href="example_code.html#example_code.linked_list.Link" title="example_code.linked_list.Link"><code class="xref py py-class docutils literal notranslate"><span class="pre">Link</span></code></a> as a
subclass of <a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a>. Instead, <em>any</em> class which
implements the <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__iter__" title="(in Python v3.14)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__iter__()</span></code></a> special method is a subclass of
<a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a>. How does this work? Well,
<a class="reference external" href="https://docs.python.org/3/library/functions.html#isinstance" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">isinstance()</span></code></a> and <a class="reference external" href="https://docs.python.org/3/library/functions.html#issubclass" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">issubclass()</span></code></a> are implemented with the help of, you
guessed it, yet another <a class="reference internal" href="3_objects.html#term-special-method"><span class="xref std std-term">special method</span></a>. This time the special method is
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABCMeta.__subclasshook__" title="(in Python v3.14)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__subclasshook__()</span></code></a>.</p>
<div class="literal-block-wrapper docutils container" id="id7">
<span id="subclasshook"></span><div class="code-block-caption"><span class="caption-number">Listing 10.3 </span><span class="caption-text">The source code for <a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">collections.abc.Iterable</span></code></a> extracted
from the <a class="reference external" href="https://github.com/python/cpython/blob/master/Lib/_collections_abc.py">Git repository for the standard Python language
implementation</a>.</span><a class="headerlink" href="#id7" title="Link to this code">¶</a></div>
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="kn">from</span><span class="w"> </span><span class="nn">abc</span><span class="w"> </span><span class="kn">import</span> <span class="n">ABCMeta</span><span class="p">,</span> <span class="n">abstractmethod</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="o">...</span>
<span class="linenos"> 4</span>
<span class="linenos"> 5</span><span class="k">def</span><span class="w"> </span><span class="nf">_check_methods</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="o">*</span><span class="n">methods</span><span class="p">):</span>
<span class="linenos"> 6</span> <span class="n">mro</span> <span class="o">=</span> <span class="n">C</span><span class="o">.</span><span class="vm">__mro__</span>
<span class="linenos"> 7</span> <span class="k">for</span> <span class="n">method</span> <span class="ow">in</span> <span class="n">methods</span><span class="p">:</span>
<span class="linenos"> 8</span> <span class="k">for</span> <span class="n">B</span> <span class="ow">in</span> <span class="n">mro</span><span class="p">:</span>
<span class="linenos"> 9</span> <span class="k">if</span> <span class="n">method</span> <span class="ow">in</span> <span class="n">B</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">:</span>
<span class="linenos">10</span> <span class="k">if</span> <span class="n">B</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="n">method</span><span class="p">]</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="linenos">11</span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="linenos">12</span> <span class="k">break</span>
<span class="linenos">13</span> <span class="k">else</span><span class="p">:</span>
<span class="linenos">14</span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="linenos">15</span> <span class="k">return</span> <span class="kc">True</span>
<span class="linenos">16</span>
<span class="linenos">17</span><span class="o">...</span>
<span class="linenos">18</span>
<span class="linenos">19</span><span class="k">class</span><span class="w"> </span><span class="nc">Iterable</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">ABCMeta</span><span class="p">):</span>
<span class="linenos">20</span>
<span class="linenos">21</span> <span class="vm">__slots__</span> <span class="o">=</span> <span class="p">()</span>
<span class="linenos">22</span>
<span class="linenos">23</span> <span class="nd">@abstractmethod</span>
<span class="linenos">24</span> <span class="k">def</span><span class="w"> </span><span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="linenos">25</span> <span class="k">while</span> <span class="kc">False</span><span class="p">:</span>
<span class="linenos">26</span> <span class="k">yield</span> <span class="kc">None</span>
<span class="linenos">27</span>
<span class="linenos">28</span> <span class="nd">@classmethod</span>
<span class="linenos">29</span> <span class="k">def</span><span class="w"> </span><span class="nf">__subclasshook__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">C</span><span class="p">):</span>
<span class="linenos">30</span> <span class="k">if</span> <span class="bp">cls</span> <span class="ow">is</span> <span class="n">Iterable</span><span class="p">:</span>
<span class="linenos">31</span> <span class="k">return</span> <span class="n">_check_methods</span><span class="p">(</span><span class="n">C</span><span class="p">,</span> <span class="s2">"__iter__"</span><span class="p">)</span>
<span class="linenos">32</span> <span class="k">return</span> <span class="bp">NotImplemented</span>
<span class="linenos">33</span>
<span class="linenos">34</span> <span class="n">__class_getitem__</span> <span class="o">=</span> <span class="nb">classmethod</span><span class="p">(</span><span class="n">GenericAlias</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p><a class="reference internal" href="#subclasshook"><span class="std std-numref">Listing 10.3</span></a> shows the actual source code for
<a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a> <a class="footnote-reference brackets" href="#python-in-python" id="id3" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a>. Let’s walk through
this. The inheritance in line 19 is essentially equivalent to inheriting from
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABC" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">abc.ABC</span></code></a>. Similarly, lines 21 and 34 are unrelated technical code. At
line 24 we see the <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__iter__" title="(in Python v3.14)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__iter__()</span></code></a> special method, decorated with
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.abstractmethod" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">abstractmethod</span></code></a>. This ensures that classes that do explicitly inherit
from <a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a> have to implement
<a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__iter__" title="(in Python v3.14)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">object.__iter__()</span></code></a>.</p>
<p>The part that currently concerns us, though, is the
declaration of <a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABCMeta.__subclasshook__" title="(in Python v3.14)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__subclasshook__()</span></code></a> at line 29.
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABCMeta.__subclasshook__" title="(in Python v3.14)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__subclasshook__()</span></code></a> is declared as <a class="reference internal" href="#term-class-method"><span class="xref std std-term">class method</span></a>. This
means that it will be passed the class itself as its first argument, in place
of the object. It is conventional to signal this difference by calling the
first parameter <code class="xref py py-obj docutils literal notranslate"><span class="pre">cls</span></code> instead of <code class="xref py py-obj docutils literal notranslate"><span class="pre">self</span></code>. The second parameter, <code class="xref py py-obj docutils literal notranslate"><span class="pre">C</span></code> is the class
to be tested.</p>
<p>In common with the special methods for arithmetic,
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABCMeta.__subclasshook__" title="(in Python v3.14)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__subclasshook__()</span></code></a> returns <a class="reference external" href="https://docs.python.org/3/library/constants.html#NotImplemented" title="(in Python v3.14)"><code class="xref py py-data docutils literal notranslate"><span class="pre">NotImplemented</span></code></a> to
indicate cases that it cannot deal with. In this case, if the current class is
not <a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a> (this would happen if the method were
called on a subclass of <a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a>) then
<a class="reference external" href="https://docs.python.org/3/library/constants.html#NotImplemented" title="(in Python v3.14)"><code class="xref py py-data docutils literal notranslate"><span class="pre">NotImplemented</span></code></a> is returned. If we really are checking <code class="xref py py-obj docutils literal notranslate"><span class="pre">C</span></code> against
<a class="reference external" href="https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable" title="(in Python v3.14)"><code class="xref py py-class docutils literal notranslate"><span class="pre">Iterable</span></code></a> then the <code class="xref py py-obj docutils literal notranslate"><span class="pre">_check_methods</span></code> helper function is
called. The fine details of how this works are a little technical, but in
essence the function loops over <code class="xref py py-obj docutils literal notranslate"><span class="pre">C</span></code> and its superclasses in order (<code class="xref py py-obj docutils literal notranslate"><span class="pre">C.__mro__</span></code>
is the <a class="reference internal" href="#term-method-resolution-order"><span class="xref std std-term">method resolution order</span></a>) and checks if the relevant methods are
defined. If they are all found then <a class="reference external" href="https://docs.python.org/3/library/constants.html#True" title="(in Python v3.14)"><code class="xref py py-data docutils literal notranslate"><span class="pre">True</span></code></a> is returned, otherwise the
result is <a class="reference external" href="https://docs.python.org/3/library/constants.html#NotImplemented" title="(in Python v3.14)"><code class="xref py py-data docutils literal notranslate"><span class="pre">NotImplemented</span></code></a>. An implementation of
<a class="reference external" href="https://docs.python.org/3/library/abc.html#abc.ABCMeta.__subclasshook__" title="(in Python v3.14)"><code class="xref py py-meth docutils literal notranslate"><span class="pre">__subclasshook__()</span></code></a> could also return <a class="reference external" href="https://docs.python.org/3/library/constants.html#False" title="(in Python v3.14)"><code class="xref py py-data docutils literal notranslate"><span class="pre">False</span></code></a> to
indicate that <code class="xref py py-obj docutils literal notranslate"><span class="pre">C</span></code> is definitely not a subclass.</p>
</section>
</section>
<section id="glossary">
<h2><span class="section-number">10.3. </span>Glossary<a class="headerlink" href="#glossary" title="Link to this heading">¶</a></h2>
<dl class="simple glossary">
<dt id="term-abstract-base-class">abstract base class<a class="headerlink" href="#term-abstract-base-class" title="Link to this term">¶</a></dt><dd><p>A class designed only to be the <a class="reference internal" href="7_inheritance.html#term-parent-class"><span class="xref std std-term">parent</span></a> of other
classes, and never to be instantiated itself. Abstract classes often
define the interfaces of <a class="reference internal" href="3_objects.html#term-method"><span class="xref std std-term">methods</span></a> but leave their implementations
to the concrete <a class="reference internal" href="7_inheritance.html#term-child-class"><span class="xref std std-term">child classes</span></a>.</p>
</dd>
<dt id="term-abstract-method">abstract method<a class="headerlink" href="#term-abstract-method" title="Link to this term">¶</a></dt><dd><p>A method whose presence is required by an <a class="reference internal" href="#term-abstract-base-class"><span class="xref std std-term">abstract base class</span></a>
but for which concrete subclasses are required to provide the implementation.</p>
</dd>
<dt id="term-class-method">class method<a class="headerlink" href="#term-class-method" title="Link to this term">¶</a></dt><dd><p>A <a class="reference internal" href="3_objects.html#term-method"><span class="xref std std-term">method</span></a> which belongs to the class itself, rather than to the
instances of the class. Class methods are declared using the
<a class="reference external" href="https://docs.python.org/3/library/functions.html#classmethod" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">classmethod</span></code></a> <a class="reference internal" href="#term-decorator"><span class="xref std std-term">decorator</span></a> and take the class (<code class="xref py py-obj docutils literal notranslate"><span class="pre">cls</span></code>) as their first
argument, instead of the instance (<code class="xref py py-obj docutils literal notranslate"><span class="pre">self</span></code>). See also: <a class="reference internal" href="7_inheritance.html#term-class-attribute"><span class="xref std std-term">class attribute</span></a>.</p>
</dd>
<dt id="term-decorator">decorator<a class="headerlink" href="#term-decorator" title="Link to this term">¶</a></dt><dd><p>A syntax for applying <a class="reference internal" href="#term-higher-order-function"><span class="xref std std-term">higher order functions</span></a> when defining functions. A decorator is applied by writing
<code class="xref py py-obj docutils literal notranslate"><span class="pre">@</span></code> followed by the decorator name immediately before the declaration
of the function or <a class="reference internal" href="3_objects.html#term-method"><span class="xref std std-term">method</span></a> to which the decorator applies.</p>
</dd>
<dt id="term-duck-typing">duck typing<a class="headerlink" href="#term-duck-typing" title="Link to this term">¶</a></dt><dd><p>The idea that the precise <a class="reference internal" href="3_objects.html#term-type"><span class="xref std std-term">type</span></a> of an <a class="reference external" href="https://docs.python.org/3/glossary.html#term-object" title="(in Python v3.14)"><span class="xref std std-term">object</span></a> is not important, it is
only important that the object has the correct <a class="reference internal" href="3_objects.html#term-method"><span class="xref std std-term">methods</span></a>
or <a class="reference internal" href="3_objects.html#term-attribute"><span class="xref std std-term">attributes</span></a> for the current operation. If an
object walks like a duck, and quacks like a duck then it can be taken
to be a duck.</p>
</dd>
<dt id="term-higher-order-function">higher order function<a class="headerlink" href="#term-higher-order-function" title="Link to this term">¶</a></dt><dd><p>A function which acts on other functions, and which possibly returns
another function as its result.</p>
</dd>
<dt id="term-method-resolution-order">method resolution order<a class="headerlink" href="#term-method-resolution-order" title="Link to this term">¶</a></dt><dt id="term-MRO">MRO<a class="headerlink" href="#term-MRO" title="Link to this term">¶</a></dt><dd><p>A sequence of the superclasses of the current class ordered by
increasing ancestry. The superclasses of a class are searched in method
resolution order to find implementations of <a class="reference internal" href="3_objects.html#term-method"><span class="xref std std-term">methods</span></a>
and <a class="reference internal" href="3_objects.html#term-attribute"><span class="xref std std-term">attributes</span></a>.</p>
</dd>
<dt id="term-syntactic-sugar">syntactic sugar<a class="headerlink" href="#term-syntactic-sugar" title="Link to this term">¶</a></dt><dd><p>A feature of the programming language which adds no new functionality,
but which enables a clearer or more concise syntax. Python
<a class="reference internal" href="3_objects.html#term-special-method"><span class="xref std std-term">special methods</span></a> are a form of syntactic sugar as they enable,
for example, the syntax <code class="xref py py-obj docutils literal notranslate"><span class="pre">a</span> <span class="pre">+</span> <span class="pre">b</span></code> instead of something like <code class="xref py py-obj docutils literal notranslate"><span class="pre">a.add(b)</span></code>.</p>
</dd>
<dt id="term-virtual-subclass">virtual subclass<a class="headerlink" href="#term-virtual-subclass" title="Link to this term">¶</a></dt><dd><p>A class which does not declare its descent from the superclass through
its definition, but which is instead claimed as a subclass by the
superclass.</p>
</dd>
</dl>
</section>
<section id="exercises">
<h2><span class="section-number">10.4. </span>Exercises<a class="headerlink" href="#exercises" title="Link to this heading">¶</a></h2>
<p>Using the information on the <a class="reference external" href="https://object-oriented-python.github.io/edition3/exercises.html">book website</a>
obtain the skeleton code for these exercises.</p>
<div class="proof proof-type-exercise" id="id8">
<div class="proof-title">
<span class="proof-type">Exercise 10.1</span>
</div><div class="proof-content">
<p>The objective of this exercise is to write a <a class="reference internal" href="#term-decorator"><span class="xref std std-term">decorator</span></a> which logs
whenever the decorated function is called. This sort of decorator could be
very useful in debugging code. Create the decorator in the
<code class="xref py py-obj docutils literal notranslate"><span class="pre">log_decorator.log_decorator</span></code> module and ensure it is importable as
<code class="xref py py-obj docutils literal notranslate"><span class="pre">log_decorator.log_call</span></code>. The decorator should be applicable to functions
taking any combination of arguments.</p>
<p>The logging itself should be accomplished using
the built-in <a class="reference external" href="https://docs.python.org/3/library/logging.html#module-logging" title="(in Python v3.14)"><code class="xref py py-obj docutils literal notranslate"><span class="pre">logging</span></code></a> module by calling <a class="reference external" href="https://docs.python.org/3/library/logging.html#logging.info" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">logging.info()</span></code></a> and passing
the log message.</p>
<p>The log message should comprise the string <code class="xref py py-obj docutils literal notranslate"><span class="pre">"Calling:</span> <span class="pre">"</span></code> followed by the
function name (accessible using the <code class="xref py py-obj docutils literal notranslate"><span class="pre">__name__</span></code> attribute), followed by
round brackets containing first the <a class="reference external" href="https://docs.python.org/3/library/functions.html#repr" title="(in Python v3.14)"><code class="xref py py-func docutils literal notranslate"><span class="pre">repr()</span></code></a> of the positional
arguments, followed by the key=value pairs the keyword arguments.</p>
</div></div><div class="proof proof-type-exercise" id="id9">
<div class="proof-title">
<span class="proof-type">Exercise 10.2</span>
</div><div class="proof-content">
<p>The <code class="xref py py-mod docutils literal notranslate"><span class="pre">groups.groups</span></code> module in the skeleton code is the new version
introduced above, using an <a class="reference internal" href="#term-abstract-base-class"><span class="xref std std-term">abstract base class</span></a>. The
<code class="xref py py-obj docutils literal notranslate"><span class="pre">log_decorator.log_call</span></code> <a class="reference internal" href="#term-decorator"><span class="xref std std-term">decorator</span></a> has been applied to the
<code class="xref py py-meth docutils literal notranslate"><span class="pre">Group._validate()</span></code> <a class="reference internal" href="#term-abstract-method"><span class="xref std std-term">abstract method</span></a>. However, even once you
have implemented this decorator, it never gets called. Your challenge is to
modify <code class="xref py py-mod docutils literal notranslate"><span class="pre">groups.groups</span></code> so that the decorator is called every time a
subclass <code class="xref py py-meth docutils literal notranslate"><span class="pre">_validate()</span></code> method is called, but <strong>without</strong> moving or
duplicating <code class="xref py py-obj docutils literal notranslate"><span class="pre">@log_call</span></code>.</p>
</div></div><p class="rubric">Footnotes</p>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="python-in-python" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="#id3">1</a><span class="fn-bracket">]</span></span>
<p>Most of the <a class="reference external" href="https://docs.python.org/3/library/index.html#library-index" title="(in Python v3.14)"><span class="xref std std-ref">Python Standard Library</span></a> is written
in Python, so diving in and reading the source code is often an option if
you really want to know how some part of the language works.</p>
</aside>
<aside class="footnote brackets" id="exercise-page" role="doc-footnote">
<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="#id4">2</a><span class="fn-bracket">]</span></span>
<p><a class="reference external" href="https://object-oriented-python.github.io/edition3/exercises.html">https://object-oriented-python.github.io/edition3/exercises.html</a></p>
</aside>
</aside>
</section>
</section>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer" role="contentinfo">
© Copyright 2019-2023, David A. Ham.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 7.4.7.
</div>
</body>
</html>