Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy path7_inheritance.html
More file actions
Latest commit
808 lines (773 loc) · 104 KB
/
Copy path7_inheritance.html
File metadata and controls
808 lines (773 loc) · 104 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
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
<!DOCTYPE html>
<htmllang="en" data-content_root="./">
<head>
<metacharset="utf-8" />
<metaname="viewport" content="width=device-width, initial-scale=1.0" /><metaname="viewport" content="width=device-width, initial-scale=1" />
<title>7. Inheritance and composition — Object-oriented Programming documentation</title>
<linkrel="stylesheet" type="text/css" href="_static/pygments.css?v=03e43079" />
<linkrel="stylesheet" type="text/css" href="_static/fenics.css?v=8c7d05f9" />
<linkrel="stylesheet" type="text/css" href="_static/proof.css" />
<linkrel="stylesheet" type="text/css" href="_static/graphviz.css?v=fd3f3429" />
<scriptsrc="_static/documentation_options.js?v=5929fcd5"></script>
<scriptsrc="_static/doctools.js?v=9a2dae69"></script>
<scriptsrc="_static/sphinx_highlight.js?v=dc90522c"></script>
<scriptsrc="_static/proof.js"></script>
<scriptasync="async" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<linkrel="index" title="Index" href="genindex.html" />
<linkrel="search" title="Search" href="search.html" />
<linkrel="next" title="8. Debugging and testing" href="8_debugging.html" />
<linkrel="prev" title="6. Errors and exceptions" href="6_exceptions.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 -->
<scriptasyncsrc="https://www.googletagmanager.com/gtag/js?id=G-0EFVH5C4DC"></script>
<script>
window.dataLayer=window.dataLayer||[];
functiongtag(){dataLayer.push(arguments);}
gtag('js',newDate());
gtag('config','G-0EFVH5C4DC');
</script>
<linkrel="stylesheet" href="_static/featured.css">
<linkrel="shortcut icon" href="_static/icon.ico" />
</head><body>
<divclass="wrapper">
<ahref="index.html"><imgsrc="_static/banner.png" width="900px" alt="FInAT Project Banner" /></a>
<divid="access">
<divclass="menu">
<ul>
<liclass="page_item"><ahref="index.html" title="Book">Book</a></li>
<liclass="page_item"><ahref="videos.html" title="Videos">Videos</a></li>
<liclass="page_item"><ahref="exercises.html"
title="Exercises">Exercises</a></li>
<liclass="page_item"><ahref="installation.html" title="Installation">Installation</a></li>
</ul>
</div><!-- .menu -->
</div><!-- #access -->
</div><!-- #wrapper -->
<divclass="document">
<divclass="documentwrapper">
<divclass="bodywrapper">
<divclass="body" role="main">
<sectionid="inheritance-and-composition">
<spanid="inheritance"></span><h1><spanclass="section-number">7. </span>Inheritance and composition<aclass="headerlink" href="#inheritance-and-composition" title="Link to this heading">¶</a></h1>
<details>
<summary>
Video: inheritance and composition.</summary><divclass="video_wrapper" style="">
<iframeallowfullscreen="true" src="https://player.vimeo.com/video/516216973" style="border: 0; height: 345px; width: 560px">
</iframe></div><p>Imperial students can also <aclass="reference external" href="https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=b40b76c6-ba53-4e0f-84e9-ae1c00db5322">watch this video on Panopto</a>.</p>
</details><p>A key feature of abstractions is <aclass="reference internal" href="#term-composition"><spanclass="xref std std-term">composability</span></a>: the
ability to make a complex object or operation out of several components. We can
compose objects by simply making one object an <aclass="reference internal" href="3_objects.html#term-attribute"><spanclass="xref std std-term">attribute</span></a> of another
object. This combines objects in a <em>has a</em> relationship. For example the
<aclass="reference internal" href="example_code.html#example_code.polynomial.Polynomial" title="example_code.polynomial.Polynomial"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code></a> class introduced in
<aclass="reference internal" href="3_objects.html#objects"><spanclass="std std-numref">Chapter 3</span></a><em>has a</em><aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#tuple" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">tuple</span></code></a> of coefficients. Object
composition of this sort is a core part of <aclass="reference internal" href="3_objects.html#term-encapsulation"><spanclass="xref std std-term">encapsulation</span></a>.</p>
<p>Another way of composing abstractions is to make a new <aclass="reference internal" href="3_objects.html#term-class"><spanclass="xref std std-term">class</span></a>
by modifying another class. Typically this is employed to make a more
specialised <aclass="reference internal" href="3_objects.html#term-class"><spanclass="xref std std-term">class</span></a> from a more general one. In fact, we have
already seen this in the case of number classes. Recall that all
numbers are instances of <aclass="reference external" href="https://docs.python.org/3/library/numbers.html#numbers.Number" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">numbers.Number</span></code></a>:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="kn">from</span><spanclass="w"></span><spanclass="nn">numbers</span><spanclass="w"></span><spanclass="kn">import</span><spanclass="n">Number</span>
<spanclass="gp">In [2]: </span><spanclass="nb">isinstance</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="n">Number</span><spanclass="p">)</span>
<spanclass="gh">Out[2]: </span><spanclass="go">True</span>
</pre></div>
</div>
<p>Hang on, though. <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">1</span></code> is actually an instance of <aclass="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">int</span></code></a>:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [3]: </span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">)</span>
<spanclass="gh">Out[3]: </span><spanclass="go">int</span>
<spanclass="gp">In [4]: </span><spanclass="nb">isinstance</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="nb">int</span><spanclass="p">)</span>
<spanclass="gh">Out[4]: </span><spanclass="go">True</span>
</pre></div>
</div>
<p>So <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">1</span></code>, it turns out, has <aclass="reference internal" href="3_objects.html#term-type"><spanclass="xref std std-term">type</span></a><aclass="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">int</span></code></a>, and so is an
instance of <aclass="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">int</span></code></a>, but is also somehow an instance of
<aclass="reference external" href="https://docs.python.org/3/library/numbers.html#numbers.Number" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">numbers.Number</span></code></a>. Mathematically, the answer is obvious:
integers form a subset of all of the numbers. Object inheritance works
in much the same way: we say that <aclass="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">int</span></code></a> is a <aclass="reference internal" href="#term-subclass"><spanclass="xref std std-term">subclass</span></a>
of <aclass="reference external" href="https://docs.python.org/3/library/numbers.html#numbers.Number" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">numbers.Number</span></code></a>. Just as <aclass="reference external" href="https://docs.python.org/3/library/functions.html#isinstance" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">isinstance()</span></code></a> provides a
mechanism for determining whether an object is an <aclass="reference internal" href="3_objects.html#term-instance"><spanclass="xref std std-term">instance</span></a> of
a <aclass="reference internal" href="3_objects.html#term-class"><spanclass="xref std std-term">class</span></a>, <aclass="reference external" href="https://docs.python.org/3/library/functions.html#issubclass" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">issubclass()</span></code></a> will tell us when one
<aclass="reference internal" href="3_objects.html#term-class"><spanclass="xref std std-term">class</span></a> is a <aclass="reference internal" href="#term-subclass"><spanclass="xref std std-term">subclass</span></a> of another:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [5]: </span><spanclass="nb">issubclass</span><spanclass="p">(</span><spanclass="nb">int</span><spanclass="p">,</span><spanclass="n">Number</span><spanclass="p">)</span>
<spanclass="gh">Out[5]: </span><spanclass="go">True</span>
</pre></div>
</div>
<p>In fact, there is a whole hierarchy of
numeric types in <aclass="reference external" href="https://docs.python.org/3/library/numbers.html#module-numbers" title="(in Python v3.14)"><codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">numbers</span></code></a>:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [6]: </span><spanclass="kn">import</span><spanclass="w"></span><spanclass="nn">numbers</span>
<spanclass="gp">In [7]: </span><spanclass="nb">issubclass</span><spanclass="p">(</span><spanclass="nb">int</span><spanclass="p">,</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Integral</span><spanclass="p">)</span>
<spanclass="gh">Out[7]: </span><spanclass="go">True</span>
<spanclass="gp">In [8]: </span><spanclass="nb">issubclass</span><spanclass="p">(</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Integral</span><spanclass="p">,</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Rational</span><spanclass="p">)</span>
<spanclass="gh">Out[8]: </span><spanclass="go">True</span>
<spanclass="gp">In [9]: </span><spanclass="nb">issubclass</span><spanclass="p">(</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Rational</span><spanclass="p">,</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Real</span><spanclass="p">)</span>
<spanclass="gh">Out[9]: </span><spanclass="go">True</span>
<spanclass="gp">In [10]: </span><spanclass="nb">issubclass</span><spanclass="p">(</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Real</span><spanclass="p">,</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Complex</span><spanclass="p">)</span>
<spanclass="gh">Out[10]: </span><spanclass="go">True</span>
</pre></div>
</div>
<p>It turns out that <aclass="reference external" href="https://docs.python.org/3/library/functions.html#issubclass" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">issubclass()</span></code></a> is reflexive (classes are subclasses of themselves):</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [11]: </span><spanclass="nb">issubclass</span><spanclass="p">(</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Real</span><spanclass="p">,</span><spanclass="n">numbers</span><spanclass="o">.</span><spanclass="n">Real</span><spanclass="p">)</span>
<spanclass="gh">Out[11]: </span><spanclass="go">True</span>
</pre></div>
</div>
<p>This means that, in a manner analogous to subset inclusion, the
<aclass="reference internal" href="#term-subclass"><spanclass="xref std std-term">subclass</span></a> relationship forms a partial order on the set of all
classes. This relationship defines another core mechanism for creating a new
class from existing classes: <aclass="reference internal" href="#term-inheritance"><spanclass="xref std std-term">inheritance</span></a>. If one class is a subclass of
another then we say that it inherits from that class. Where composition defines
a <em>has a</em> relationship, inheritance defines an <em>is a</em> relationship.</p>
<sectionid="an-example-from-group-theory">
<h2><spanclass="section-number">7.1. </span>An example from group theory<aclass="headerlink" href="#an-example-from-group-theory" title="Link to this heading">¶</a></h2>
<details>
<summary>
Video: an example from group theory.</summary><divclass="video_wrapper" style="">
<iframeallowfullscreen="true" src="https://player.vimeo.com/video/516277973" style="border: 0; height: 345px; width: 560px">
</iframe></div><p>Imperial students can also <aclass="reference external" href="https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=1110c00a-dba2-4d7b-895a-ae1c00db630f">watch this video on Panopto</a>.</p>
</details><p>In order to illustrate how composition and inheritance work, let’s suppose that
we want to write a module that implements some basic groups. Recall that a group
is a collection of elements, and a group operation which obeys certain axioms.
A computer implementation of a group might therefore involve objects
representing groups, and objects representing elements. We’ll lay out one
possible configuration, which helpfully involves both inheritance and
composition, as well as parametrisation of objects and delegation of methods.</p>
<sectionid="cyclic-groups">
<h3><spanclass="section-number">7.1.1. </span>Cyclic groups<aclass="headerlink" href="#cyclic-groups" title="Link to this heading">¶</a></h3>
<p>Let’s start with the cyclic groups of order <spanclass="math notranslate nohighlight">\(n\)</span>. These are isomorphic to
the integers under addition modulo <spanclass="math notranslate nohighlight">\(n\)</span>, a property which we can use to
create our implementation. We’re going to eventually want to make different
types of groups, so we’re going to need to carefully consider what changes from
group to group, and what is the same. The first thing that we observe is that
different cyclic groups differ only by their order, so we could quite easily
have a single cyclic group class, and set the order when we <aclass="reference internal" href="3_objects.html#term-instantiate"><spanclass="xref std std-term">instantiate</span></a>
it. This is pretty common: groups often come in families defined by some sort of
size parameter. A group is defined by what values its elements can take, and the
group operation. We might therefore be tempted to think that we need to define a
cyclic group element type which can take the relevant values and which
implements the group operation. This would be unfortunate for at least two
reasons:</p>
<olclass="arabic simple">
<li><p>Because each group needs several elements, we would need a different element <em>type</em>
for each <em>instance</em> of a cyclic group. The number of classes needed would grow very fast!</p></li>
<li><p>Adding a new family of groups would require us to add both a group class and a
set of element classes. On the basis of <aclass="reference internal" href="4_style.html#term-parsimony"><spanclass="xref std std-term">parsimony</span></a>,
we would much prefer to only add one class in order to add a new family of
groups.</p></li>
</ol>
<p>Instead, we can make a single generic element type, and pass the group as an
<aclass="reference external" href="https://docs.python.org/3/glossary.html#term-argument" title="(in Python v3.14)"><spanclass="xref std std-term">argument</span></a> when instantiating the element. This is an example of
<aclass="reference internal" href="#term-composition"><spanclass="xref std std-term">composition</span></a>: each element <em>has a</em> group. The group will then implement
methods which check that element values are allowed for that group, and a method
which implements the group operation. Element objects will then <aclass="reference internal" href="#term-delegation"><spanclass="xref std std-term">delegate</span></a> validation and the group operation back to the group object.</p>
<p>Finally, we will want an <aclass="reference internal" href="3_objects.html#term-infix-operator"><spanclass="xref std std-term">infix operator</span></a> representing the group
operation. Group theorists often use a dot, but we need to choose one of the
infix operators that Python supports. We’ll choose <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">*</span></code>, which is possibly the
closest match among Python’s operators. One could easily envisage a more
complete implementation of a group, with support for group properties such as
generators and element features such as inverses. Our objective here is to
develop an understanding of class relations, rather than of algebra, so this
minimal characterisation of a group will suffice.</p>
<divclass="literal-block-wrapper docutils container" id="cyclic-group">
<divclass="code-block-caption"><spanclass="caption-number">Listing 7.1 </span><spanclass="caption-text">A simple implementation of a cyclic group class, and a generic
group element.</span><aclass="headerlink" href="#cyclic-group" title="Link to this code">¶</a></div>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos"> 7</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">Element</span><spanclass="p">:</span>
<spanclass="linenos"> 8</span><spanclass="w"></span><spanclass="sd">"""An element of the specified group.</span>
<spanclass="linenos"> 9</span>
<spanclass="linenos">10</span><spanclass="sd"> Parameters</span>
<spanclass="linenos">11</span><spanclass="sd"> ----------</span>
<spanclass="linenos">12</span><spanclass="sd"> group:</span>
<spanclass="linenos">13</span><spanclass="sd"> The group of which this is an element.</span>
<spanclass="linenos">14</span><spanclass="sd"> value:</span>
<spanclass="linenos">15</span><spanclass="sd"> The individual element value.</span>
<spanclass="linenos">16</span><spanclass="sd"> """</span>
<spanclass="linenos">17</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">group</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="linenos">18</span><spanclass="n">group</span><spanclass="o">.</span><spanclass="n">_validate</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="p">)</span>
<spanclass="linenos">19</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">group</span><spanclass="o">=</span><spanclass="n">group</span>
<spanclass="linenos">20</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">value</span><spanclass="o">=</span><spanclass="n">value</span>
<spanclass="linenos">21</span>
<spanclass="linenos">22</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__mul__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">other</span><spanclass="p">):</span>
<spanclass="linenos">23</span><spanclass="w"></span><spanclass="sd">"""Use * to represent the group operation."""</span>
<spanclass="linenos">24</span><spanclass="k">return</span><spanclass="n">Element</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">group</span><spanclass="p">,</span>
<spanclass="linenos">25</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">group</span><spanclass="o">.</span><spanclass="n">operation</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">value</span><spanclass="p">,</span><spanclass="n">other</span><spanclass="o">.</span><spanclass="n">value</span><spanclass="p">))</span>
<spanclass="linenos">26</span>
<spanclass="linenos">27</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__str__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">28</span><spanclass="w"></span><spanclass="sd">"""Return a string of the form value_group."""</span>
<spanclass="linenos">29</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">value</span><spanclass="si">}</span><spanclass="s2">_</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">group</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="linenos">30</span>
<spanclass="linenos">31</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__repr__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">32</span><spanclass="w"></span><spanclass="sd">"""Return the canonical string representation of the element."""</span>
<spanclass="linenos">33</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">)</span><spanclass="o">.</span><spanclass="vm">__name__</span><spanclass="si">}{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">group</span><spanclass="p">,</span><spanclass="w"></span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">value</span><spanclass="si">!r}</span><spanclass="s2">"</span>
<spanclass="linenos">34</span>
<spanclass="linenos">35</span>
<spanclass="linenos">36</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">CyclicGroup</span><spanclass="p">:</span>
<spanclass="linenos">37</span><spanclass="w"></span><spanclass="sd">"""A cyclic group represented by addition modulo group order."""</span>
<spanclass="linenos">38</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">order</span><spanclass="p">):</span>
<spanclass="linenos">39</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">order</span><spanclass="o">=</span><spanclass="n">order</span>
<spanclass="linenos">40</span>
<spanclass="linenos">41</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">_validate</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="linenos">42</span><spanclass="w"></span><spanclass="sd">"""Ensure that value is an allowed element value in this group."""</span>
<spanclass="linenos">43</span><spanclass="k">if</span><spanclass="ow">not</span><spanclass="p">(</span><spanclass="nb">isinstance</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="p">,</span><spanclass="n">Integral</span><spanclass="p">)</span><spanclass="ow">and</span><spanclass="mi">0</span><spanclass="o"><=</span><spanclass="n">value</span><spanclass="o"><</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">order</span><spanclass="p">):</span>
<spanclass="linenos">44</span><spanclass="k">raise</span><spanclass="ne">ValueError</span><spanclass="p">(</span><spanclass="s2">"Element value must be an integer"</span>
<spanclass="linenos">45</span><spanclass="sa">f</span><spanclass="s2">" in the range [0, </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">order</span><spanclass="si">}</span><spanclass="s2">)"</span><spanclass="p">)</span>
<spanclass="linenos">46</span>
<spanclass="linenos">47</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">operation</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">a</span><spanclass="p">,</span><spanclass="n">b</span><spanclass="p">):</span>
<spanclass="linenos">48</span><spanclass="w"></span><spanclass="sd">"""Perform the group operation on two values.</span>
<spanclass="linenos">49</span>
<spanclass="linenos">50</span><spanclass="sd"> The group operation is addition modulo n.</span>
<spanclass="linenos">51</span><spanclass="sd"> """</span>
<spanclass="linenos">52</span><spanclass="k">return</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span><spanclass="o">%</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">order</span>
<spanclass="linenos">53</span>
<spanclass="linenos">54</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__call__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="linenos">55</span><spanclass="w"></span><spanclass="sd">"""Create an element of this group."""</span>
<spanclass="linenos">56</span><spanclass="k">return</span><spanclass="n">Element</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">)</span>
<spanclass="linenos">57</span>
<spanclass="linenos">58</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__str__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">59</span><spanclass="w"></span><spanclass="sd">"""Represent the group as Gd."""</span>
<spanclass="linenos">60</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"C</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">order</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="linenos">61</span>
<spanclass="linenos">62</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__repr__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">63</span><spanclass="w"></span><spanclass="sd">"""Return the canonical string representation of the group."""</span>
<spanclass="linenos">64</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">)</span><spanclass="o">.</span><spanclass="vm">__name__</span><spanclass="si">}</span><spanclass="s2">(</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">order</span><spanclass="si">!r}</span><spanclass="s2">)"</span>
</pre></div>
</div>
</div>
<p><aclass="reference internal" href="#cyclic-group"><spanclass="std std-numref">Listing 7.1</span></a> shows an implementation of our minimal conception of
cyclic groups. Before considering it in any detail let’s try it out to observe
the concrete effects of the classes:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="kn">from</span><spanclass="w"></span><spanclass="nn">example_code.groups_basic</span><spanclass="w"></span><spanclass="kn">import</span><spanclass="n">CyclicGroup</span>
<spanclass="gp">In [2]: </span><spanclass="n">C</span><spanclass="o">=</span><spanclass="n">CyclicGroup</span><spanclass="p">(</span><spanclass="mi">5</span><spanclass="p">)</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">C</span><spanclass="p">(</span><spanclass="mi">3</span><spanclass="p">)</span><spanclass="o">*</span><spanclass="n">C</span><spanclass="p">(</span><spanclass="mi">4</span><spanclass="p">))</span>
<spanclass="go">2_C5</span>
</pre></div>
</div>
<p>We observe that we are able to create the cyclic group of order 5. Due to the
definition of the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__call__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__call__()</span></code></a><aclass="reference internal" href="3_objects.html#term-special-method"><spanclass="xref std std-term">special method</span></a> at line 49,
we are then able to create elements of the group by calling the group object.
The group operation then has the expected effect:</p>
<divclass="math notranslate nohighlight" id="equation-7-inheritance-0">
<spanclass="eqno">(7.1)<aclass="headerlink" href="#equation-7-inheritance-0" title="Link to this equation">¶</a></span>\[\begin{split}3_{C_5} \cdot 4_{C_5} &\equiv (3 + 4) \operatorname{mod} 5\\
&= 2\\
&\equiv 2_{C_5}\end{split}\]</div>
<p>Finally, if we attempt to make a group element with a value which is not an
integer between 0 and 5, an exception is raised.</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [4]: </span><spanclass="n">C</span><spanclass="p">(</span><spanclass="mf">1.5</span><spanclass="p">)</span>
<spanclass="gt">--------------------------------------------------------------------------</span>
<spanclass="ne">ValueError</span><spanclass="g g-Whitespace"></span>Traceback (most recent call last)
<spanclass="n">Cell</span><spanclass="n">In</span><spanclass="p">[</span><spanclass="mi">4</span><spanclass="p">],</span><spanclass="n">line</span><spanclass="mi">1</span>
<spanclass="ne">----> </span><spanclass="mi">1</span><spanclass="n">C</span><spanclass="p">(</span><spanclass="mf">1.5</span><spanclass="p">)</span>
<spanclass="nn">File ~/docs/principles_of_programming/object-oriented-programming/example_code/groups_basic.py:56,</span> in <spanclass="ni">CyclicGroup.__call__</span><spanclass="nt">(self, value)</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">54</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__call__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">55</span><spanclass="w"></span><spanclass="sd">"""Create an element of this group."""</span>
<spanclass="ne">---> </span><spanclass="mi">56</span><spanclass="k">return</span><spanclass="n">Element</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">)</span>
<spanclass="nn">File ~/docs/principles_of_programming/object-oriented-programming/example_code/groups_basic.py:18,</span> in <spanclass="ni">Element.__init__</span><spanclass="nt">(self, group, value)</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">17</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">group</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="ne">---> </span><spanclass="mi">18</span><spanclass="n">group</span><spanclass="o">.</span><spanclass="n">_validate</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="p">)</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">19</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">group</span><spanclass="o">=</span><spanclass="n">group</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">20</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">value</span><spanclass="o">=</span><spanclass="n">value</span>
<spanclass="nn">File ~/docs/principles_of_programming/object-oriented-programming/example_code/groups_basic.py:44,</span> in <spanclass="ni">CyclicGroup._validate</span><spanclass="nt">(self, value)</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">42</span><spanclass="w"></span><spanclass="sd">"""Ensure that value is an allowed element value in this group."""</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">43</span><spanclass="k">if</span><spanclass="ow">not</span><spanclass="p">(</span><spanclass="nb">isinstance</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="p">,</span><spanclass="n">Integral</span><spanclass="p">)</span><spanclass="ow">and</span><spanclass="mi">0</span><spanclass="o"><=</span><spanclass="n">value</span><spanclass="o"><</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">order</span><spanclass="p">):</span>
<spanclass="ne">---> </span><spanclass="mi">44</span><spanclass="k">raise</span><spanclass="ne">ValueError</span><spanclass="p">(</span><spanclass="s2">"Element value must be an integer"</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">45</span><spanclass="sa">f</span><spanclass="s2">" in the range [0, </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">order</span><spanclass="si">}</span><spanclass="s2">)"</span><spanclass="p">)</span>
<spanclass="ne">ValueError</span>: Element value must be an integer in the range [0, 5)
</pre></div>
</div>
<p><aclass="reference internal" href="#cyclic-group"><spanclass="std std-numref">Listing 7.1</span></a> illustrates <aclass="reference internal" href="#term-composition"><spanclass="xref std std-term">composition</span></a>: on line 19
<aclass="reference internal" href="example_code.html#example_code.groups_basic.Element" title="example_code.groups_basic.Element"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Element</span></code></a> is associated with a group object.
This is a classic <em>has a</em> relationship: an element has a group. We might have
attempted to construct this the other way around with groups having elements,
however this would have immediately hit the issue that elements have exactly
one group, while a group might have an unlimited number of elements. Object
composition is typically most successful when the relationship is uniquely
defined.</p>
<p>This code also demonstrates <aclass="reference internal" href="#term-delegation"><spanclass="xref std std-term">delegation</span></a>. In order to avoid having to
define different element classes for different groups, the element class does
not in substance implement either value validation, or the group operation.
Instead, at line 18, validation is delegated to the group by calling
<codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">group._validate()</span></code> and at line 25 the implementation of the group
operation is delegated to the group by calling <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">self.group.operation()</span></code>.</p>
</section>
<sectionid="general-linear-groups">
<h3><spanclass="section-number">7.1.2. </span>General linear groups<aclass="headerlink" href="#general-linear-groups" title="Link to this heading">¶</a></h3>
<details>
<summary>
Video: inheritance.</summary><divclass="video_wrapper" style="">
<iframeallowfullscreen="true" src="https://player.vimeo.com/video/516698411" style="border: 0; height: 345px; width: 560px">
</iframe></div><p>Imperial students can also <aclass="reference external" href="https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=14c473ff-f126-4348-b6d5-ae1c00db6c07">watch this video on Panopto</a>.</p>
</details><p>We still haven’t encountered inheritance, though. Where does that come into the
story? Well first we’ll need to introduce at least one more family of groups.
For no other reason than convenience, let’s choose <spanclass="math notranslate nohighlight">\(G_n\)</span>, the general
linear group of degree <spanclass="math notranslate nohighlight">\(n\)</span>. The elements of this group can be represented
as <spanclass="math notranslate nohighlight">\(n\times n\)</span> invertible square matrices. At least to the extent that
real numbers can be represented on a computer, we can implement this group as
follows:</p>
<divclass="literal-block-wrapper docutils container" id="general-linear-group">
<divclass="code-block-caption"><spanclass="caption-number">Listing 7.2 </span><spanclass="caption-text">A basic implementation of the general linear group of a given
degree.</span><aclass="headerlink" href="#general-linear-group" title="Link to this code">¶</a></div>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos"> 1</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">GeneralLinearGroup</span><spanclass="p">:</span>
<spanclass="linenos"> 2</span><spanclass="w"></span><spanclass="sd">"""The general linear group represented by degree square matrices."""</span>
<spanclass="linenos"> 3</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">degree</span><spanclass="p">):</span>
<spanclass="linenos"> 4</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">degree</span><spanclass="o">=</span><spanclass="n">degree</span>
<spanclass="linenos"> 5</span>
<spanclass="linenos"> 6</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">_validate</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="linenos"> 7</span><spanclass="w"></span><spanclass="sd">"""Ensure that value is an allowed element value in this group."""</span>
<spanclass="linenos"> 8</span><spanclass="n">value</span><spanclass="o">=</span><spanclass="n">np</span><spanclass="o">.</span><spanclass="n">asarray</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="p">)</span>
<spanclass="linenos"> 9</span><spanclass="k">if</span><spanclass="ow">not</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="o">.</span><spanclass="n">shape</span><spanclass="o">==</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="p">,</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="p">)):</span>
<spanclass="linenos">10</span><spanclass="k">raise</span><spanclass="ne">ValueError</span><spanclass="p">(</span><spanclass="s2">"Element value must be a "</span>
<spanclass="linenos">11</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">}</span><spanclass="s2"> x </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="linenos">12</span><spanclass="s2">"square array."</span><spanclass="p">)</span>
<spanclass="linenos">13</span>
<spanclass="linenos">14</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">operation</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">a</span><spanclass="p">,</span><spanclass="n">b</span><spanclass="p">):</span>
<spanclass="linenos">15</span><spanclass="w"></span><spanclass="sd">"""Perform the group operation on two values.</span>
<spanclass="linenos">16</span>
<spanclass="linenos">17</span><spanclass="sd"> The group operation is matrix multiplication.</span>
<spanclass="linenos">18</span><spanclass="sd"> """</span>
<spanclass="linenos">19</span><spanclass="k">return</span><spanclass="n">a</span><spanclass="o">@</span><spanclass="n">b</span>
<spanclass="linenos">20</span>
<spanclass="linenos">21</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__call__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="linenos">22</span><spanclass="w"></span><spanclass="sd">"""Create an element of this group."""</span>
<spanclass="linenos">23</span><spanclass="k">return</span><spanclass="n">Element</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">)</span>
<spanclass="linenos">24</span>
<spanclass="linenos">25</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__str__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">26</span><spanclass="w"></span><spanclass="sd">"""Represent the group as Gd."""</span>
<spanclass="linenos">27</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"G</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">degree</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="linenos">28</span>
<spanclass="linenos">29</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__repr__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">30</span><spanclass="w"></span><spanclass="sd">"""Return the canonical string representation of the group."""</span>
<spanclass="linenos">31</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">)</span><spanclass="o">.</span><spanclass="vm">__name__</span><spanclass="si">}</span><spanclass="s2">(</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">degree</span><spanclass="si">!r}</span><spanclass="s2">)"</span>
</pre></div>
</div>
</div>
<p>We won’t illustrate the operation of this class, though the reader is welcome to
<aclass="reference external" href="https://docs.python.org/3/reference/simple_stmts.html#import" title="(in Python v3.14)"><codeclass="xref std std-keyword docutils literal notranslate"><spanclass="pre">import</span></code></a> the <aclass="reference internal" href="example_code.html#module-example_code.groups_basic" title="example_code.groups_basic"><codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">example_code.groups_basic</span></code></a> module and experiment.
Instead, we simply note that this code is very, very similar to the
implementation of <aclass="reference internal" href="example_code.html#example_code.groups_basic.CyclicGroup" title="example_code.groups_basic.CyclicGroup"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code></a> in
<aclass="reference internal" href="#cyclic-group"><spanclass="std std-numref">Listing 7.1</span></a>. The only functionally important differences are the
definitions of the <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">_validate()</span></code> and <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">operation()</span></code> methods.
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self.order</span></code> is also renamed as <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self.degree</span></code>, and <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">C</span></code> is replaced by <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">G</span></code> in the
string representation. It remains the case that there is a large amount of
code repetition between classes. For the reasons we touched on in
<aclass="reference internal" href="4_style.html#repetition"><spanclass="std std-numref">Section 4.5.4</span></a>, this is a highly undesirable state of affairs.</p>
</section>
</section>
<sectionid="id1">
<h2><spanclass="section-number">7.2. </span>Inheritance<aclass="headerlink" href="#id1" title="Link to this heading">¶</a></h2>
<p>Suppose, instead of copying much of the same code, we had a prototype
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code> class, and <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code> and <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">GeneralLinearGroup</span></code>
simply specified the ways in which they differ from the prototype. This would
avoid the issues associated with repeating code, and would make it obvious how
the different group implementations differ. This is exactly what inheritance
does.</p>
<divclass="literal-block-wrapper docutils container" id="groups-inheritance">
<divclass="code-block-caption"><spanclass="caption-number">Listing 7.3 </span><spanclass="caption-text">Implementation of a base class for a generic group, and
subclasses for the cyclic groups and general linear groups. This code
is available in the book repository in <codeclass="file docutils literal notranslate"><spanclass="pre">example_code/groups.py</span></code></span><aclass="headerlink" href="#groups-inheritance" title="Link to this code">¶</a></div>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos"> 1</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">Group</span><spanclass="p">:</span>
<spanclass="linenos"> 2</span><spanclass="w"></span><spanclass="sd">"""A base class containing methods common to many groups.</span>
<spanclass="linenos"> 3</span>
<spanclass="linenos"> 4</span><spanclass="sd"> Each subclass represents a family of parametrised groups.</span>
<spanclass="linenos"> 5</span>
<spanclass="linenos"> 6</span><spanclass="sd"> Parameters</span>
<spanclass="linenos"> 7</span><spanclass="sd"> ----------</span>
<spanclass="linenos"> 8</span><spanclass="sd"> n: int</span>
<spanclass="linenos"> 9</span><spanclass="sd"> The primary group parameter, such as order or degree. The</span>
<spanclass="linenos">10</span><spanclass="sd"> precise meaning of n changes from subclass to subclass.</span>
<spanclass="linenos">11</span><spanclass="sd"> """</span>
<spanclass="linenos">12</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">n</span><spanclass="p">):</span>
<spanclass="linenos">13</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="o">=</span><spanclass="n">n</span>
<spanclass="linenos">14</span>
<spanclass="linenos">15</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__call__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="linenos">16</span><spanclass="w"></span><spanclass="sd">"""Create an element of this group."""</span>
<spanclass="linenos">17</span><spanclass="k">return</span><spanclass="n">Element</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">)</span>
<spanclass="linenos">18</span>
<spanclass="linenos">19</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__str__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">20</span><spanclass="w"></span><spanclass="sd">"""Return a string in the form symbol then group parameter."""</span>
<spanclass="linenos">21</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">symbol</span><spanclass="si">}{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="linenos">22</span>
<spanclass="linenos">23</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__repr__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">24</span><spanclass="w"></span><spanclass="sd">"""Return the canonical string representation of the element."""</span>
<spanclass="linenos">25</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">)</span><spanclass="o">.</span><spanclass="vm">__name__</span><spanclass="si">}</span><spanclass="s2">(</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">!r}</span><spanclass="s2">)"</span>
<spanclass="linenos">26</span>
<spanclass="linenos">27</span>
<spanclass="linenos">28</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">CyclicGroup</span><spanclass="p">(</span><spanclass="n">Group</span><spanclass="p">):</span>
<spanclass="linenos">29</span><spanclass="w"></span><spanclass="sd">"""A cyclic group represented by integer addition modulo n."""</span>
<spanclass="linenos">30</span><spanclass="n">symbol</span><spanclass="o">=</span><spanclass="s2">"C"</span>
<spanclass="linenos">31</span>
<spanclass="linenos">32</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">_validate</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="linenos">33</span><spanclass="w"></span><spanclass="sd">"""Ensure that value is an allowed element value in this group."""</span>
<spanclass="linenos">34</span><spanclass="k">if</span><spanclass="ow">not</span><spanclass="p">(</span><spanclass="nb">isinstance</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="p">,</span><spanclass="n">Integral</span><spanclass="p">)</span><spanclass="ow">and</span><spanclass="mi">0</span><spanclass="o"><=</span><spanclass="n">value</span><spanclass="o"><</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="p">):</span>
<spanclass="linenos">35</span><spanclass="k">raise</span><spanclass="ne">ValueError</span><spanclass="p">(</span><spanclass="s2">"Element value must be an integer"</span>
<spanclass="linenos">36</span><spanclass="sa">f</span><spanclass="s2">" in the range [0, </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">}</span><spanclass="s2">)"</span><spanclass="p">)</span>
<spanclass="linenos">37</span>
<spanclass="linenos">38</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">operation</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">a</span><spanclass="p">,</span><spanclass="n">b</span><spanclass="p">):</span>
<spanclass="linenos">39</span><spanclass="w"></span><spanclass="sd">"""Perform the group operation on two values.</span>
<spanclass="linenos">40</span>
<spanclass="linenos">41</span><spanclass="sd"> The group operation is addition modulo n.</span>
<spanclass="linenos">42</span><spanclass="sd"> """</span>
<spanclass="linenos">43</span><spanclass="k">return</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span><spanclass="o">%</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span>
<spanclass="linenos">44</span>
<spanclass="linenos">45</span>
<spanclass="linenos">46</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">GeneralLinearGroup</span><spanclass="p">(</span><spanclass="n">Group</span><spanclass="p">):</span>
<spanclass="linenos">47</span><spanclass="w"></span><spanclass="sd">"""The general linear group represented by n x n matrices."""</span>
<spanclass="linenos">48</span>
<spanclass="linenos">49</span><spanclass="n">symbol</span><spanclass="o">=</span><spanclass="s2">"G"</span>
<spanclass="linenos">50</span>
<spanclass="linenos">51</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">_validate</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">value</span><spanclass="p">):</span>
<spanclass="linenos">52</span><spanclass="w"></span><spanclass="sd">"""Ensure that value is an allowed element value in this group."""</span>
<spanclass="linenos">53</span><spanclass="n">value</span><spanclass="o">=</span><spanclass="n">np</span><spanclass="o">.</span><spanclass="n">asarray</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="p">)</span>
<spanclass="linenos">54</span><spanclass="k">if</span><spanclass="ow">not</span><spanclass="p">(</span><spanclass="n">value</span><spanclass="o">.</span><spanclass="n">shape</span><spanclass="o">==</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="p">,</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="p">)):</span>
<spanclass="linenos">55</span><spanclass="k">raise</span><spanclass="ne">ValueError</span><spanclass="p">(</span><spanclass="s2">"Element value must be a "</span>
<spanclass="linenos">56</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">}</span><spanclass="s2"> x </span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="linenos">57</span><spanclass="s2">"square array."</span><spanclass="p">)</span>
<spanclass="linenos">58</span>
<spanclass="linenos">59</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">operation</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">a</span><spanclass="p">,</span><spanclass="n">b</span><spanclass="p">):</span>
<spanclass="linenos">60</span><spanclass="w"></span><spanclass="sd">"""Perform the group operation on two values.</span>
<spanclass="linenos">61</span>
<spanclass="linenos">62</span><spanclass="sd"> The group operation is matrix multiplication.</span>
<spanclass="linenos">63</span><spanclass="sd"> """</span>
<spanclass="linenos">64</span><spanclass="k">return</span><spanclass="n">a</span><spanclass="o">@</span><spanclass="n">b</span>
</pre></div>
</div>
</div>
<p><aclass="reference internal" href="#groups-inheritance"><spanclass="std std-numref">Listing 7.3</span></a> shows a new implementation of
<aclass="reference internal" href="example_code.html#example_code.groups.CyclicGroup" title="example_code.groups.CyclicGroup"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code></a> and
<aclass="reference internal" href="example_code.html#example_code.groups.GeneralLinearGroup" title="example_code.groups.GeneralLinearGroup"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">GeneralLinearGroup</span></code></a>. These are functionally
equivalent to those presented in <aclass="reference internal" href="#cyclic-group"><spanclass="std std-numref">Listing 7.1</span></a> and
<aclass="reference internal" href="#general-linear-group"><spanclass="std std-numref">Listing 7.2</span></a> but have all the repeated code removed. The code
common to both families of groups is instead placed in the
<aclass="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code></a> class. In the following sections we will
highlight the features of this code which make this work.</p>
<sectionid="inheritance-syntax">
<h3><spanclass="section-number">7.2.1. </span>Inheritance syntax<aclass="headerlink" href="#inheritance-syntax" title="Link to this heading">¶</a></h3>
<p>Look again at the definition of <aclass="reference internal" href="example_code.html#example_code.groups.CyclicGroup" title="example_code.groups.CyclicGroup"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code></a> on
line 28:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos">28</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">CyclicGroup</span><spanclass="p">(</span><spanclass="n">Group</span><spanclass="p">):</span>
</pre></div>
</div>
<p>This differs from the previous class definitions we’ve seen in that the
name of the class we’re defining, <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code> is followed by another
class name in brackets, <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code>. This <aclass="reference internal" href="6_exceptions.html#term-syntax"><spanclass="xref std std-term">syntax</span></a> is how inheritance
is defined. It means that <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code> is a <aclass="reference internal" href="#term-child-class"><spanclass="xref std std-term">child class</span></a> of
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code>. The effect of this is that any <aclass="reference internal" href="3_objects.html#term-attribute"><spanclass="xref std std-term">attribute</span></a> defined on the
<aclass="reference internal" href="#term-parent-class"><spanclass="xref std std-term">parent class</span></a> is also defined (is <em>inherited</em>) on the child class. In
this case, <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code> does not define <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code>,
<codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__call__()</span></code>, <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__str__()</span></code>, or <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__repr__()</span></code>. If and when any of those
<aclass="reference internal" href="3_objects.html#term-method"><spanclass="xref std std-term">methods</span></a> are called, it is the methods from the parent class,
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code> which are used. This is the mechanism that enables methods to be
shared by different classes. In this case,
<aclass="reference internal" href="example_code.html#example_code.groups.CyclicGroup" title="example_code.groups.CyclicGroup"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code></a> and
<aclass="reference internal" href="example_code.html#example_code.groups.GeneralLinearGroup" title="example_code.groups.GeneralLinearGroup"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">GeneralLinearGroup</span></code></a> share these methods. A user
could also define another class which inherited from
<aclass="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code></a>, for example to implement another family of
groups.</p>
</section>
<sectionid="class-attributes">
<h3><spanclass="section-number">7.2.2. </span>Class attributes<aclass="headerlink" href="#class-attributes" title="Link to this heading">¶</a></h3>
<p>At line 30 of <aclass="reference internal" href="#groups-inheritance"><spanclass="std std-numref">Listing 7.3</span></a>, the name <codeclass="xref py py-attr docutils literal notranslate"><spanclass="pre">symbol</span></code> is
assigned to:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos">30</span><spanclass="n">symbol</span><spanclass="o">=</span><spanclass="s2">"C"</span>
</pre></div>
</div>
<p>This is also different from our previous experience: usually if we
want to set a value on an object then we do so from inside a method, and we set
a <aclass="reference internal" href="3_objects.html#term-data-attribute"><spanclass="xref std std-term">data attribute</span></a> on the current instance, <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code>, using the syntax:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">symbol</span><spanclass="o">=</span><spanclass="s2">"C"</span>
</pre></div>
</div>
<p>This more familiar code sets an instance attribute. In other words, an attribute
specific to each object of the class. Our new version of the code instead sets a
single attribute that is common to all objects of this class. This is called a
<aclass="reference internal" href="#term-class-attribute"><spanclass="xref std std-term">class attribute</span></a>.</p>
</section>
<sectionid="attributes-resolve-at-runtime">
<spanid="runtime-attributes"></span><h3><spanclass="section-number">7.2.3. </span>Attributes resolve at runtime<aclass="headerlink" href="#attributes-resolve-at-runtime" title="Link to this heading">¶</a></h3>
<p>Consider the <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__str__()</span></code> method of <aclass="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code></a>:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos">19</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__str__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">20</span><spanclass="w"></span><spanclass="sd">"""Return a string in the form symbol then group parameter."""</span>
<spanclass="linenos">21</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">symbol</span><spanclass="si">}{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">}</span><spanclass="s2">"</span>
</pre></div>
</div>
<p>This code uses <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self.symbol</span></code>, but this attribute isn’t defined anywhere on
<aclass="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code></a>. Why doesn’t this cause an
<aclass="reference external" href="https://docs.python.org/3/library/exceptions.html#AttributeError" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">AttributeError</span></code></a> to be raised? One answer is that it indeed would if we
were to instantiate <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code> itself:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="kn">from</span><spanclass="w"></span><spanclass="nn">example_code.groups</span><spanclass="w"></span><spanclass="kn">import</span><spanclass="n">Group</span>
<spanclass="gp">In [2]: </span><spanclass="n">g</span><spanclass="o">=</span><spanclass="n">Group</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">)</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">g</span><spanclass="p">)</span>
<spanclass="gt">--------------------------------------------------------------------------</span>
<spanclass="ne">AttributeError</span><spanclass="g g-Whitespace"></span>Traceback (most recent call last)
<spanclass="n">Cell</span><spanclass="n">In</span><spanclass="p">[</span><spanclass="mi">3</span><spanclass="p">],</span><spanclass="n">line</span><spanclass="mi">1</span>
<spanclass="ne">----> </span><spanclass="mi">1</span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">g</span><spanclass="p">)</span>
<spanclass="nn">File ~/docs/principles_of_programming/object-oriented-programming/example_code/groups.py:62,</span> in <spanclass="ni">Group.__str__</span><spanclass="nt">(self)</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">60</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__str__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="g g-Whitespace"></span><spanclass="mi">61</span><spanclass="w"></span><spanclass="sd">"""Return a string in the form symbol then group parameter."""</span>
<spanclass="ne">---> </span><spanclass="mi">62</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">symbol</span><spanclass="si">}{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="ne">AttributeError</span>: 'Group' object has no attribute 'symbol'
</pre></div>
</div>
<p>In fact, <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code> is never supposed to be instantiated, it plays the role
of an <aclass="reference internal" href="10_further_object-oriented_features.html#term-abstract-base-class"><spanclass="xref std std-term">abstract base class</span></a>. In other words, its role is to provide
functionality to classes that inherit from it, rather than to be the type of
objects itself. We will return to this in more detail in
<aclass="reference internal" href="10_further_object-oriented_features.html#abstract-base-classes"><spanclass="std std-numref">Section 10.2</span></a>.</p>
<p>However, if we instead instantiate <aclass="reference internal" href="example_code.html#example_code.groups.CyclicGroup" title="example_code.groups.CyclicGroup"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code></a>
then everything works:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="kn">from</span><spanclass="w"></span><spanclass="nn">example_code.groups</span><spanclass="w"></span><spanclass="kn">import</span><spanclass="n">CyclicGroup</span>
<spanclass="gp">In [2]: </span><spanclass="n">g</span><spanclass="o">=</span><spanclass="n">CyclicGroup</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">)</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">g</span><spanclass="p">)</span>
<spanclass="go">C1</span>
</pre></div>
</div>
<p>The reason is that the code in methods is only executed when that method is
called, and the object <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code> is the actual concrete class instance, with all of
the attributes that are defined for it. In this case, even though
<codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__str__()</span></code> is defined on <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code>, <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code> has type
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">CyclicGroup</span></code>, and therefore <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self.symbol</span></code> is well-defined and has the
value <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">"C"</span></code>.</p>
</section>
<sectionid="parametrising-over-class">
<h3><spanclass="section-number">7.2.4. </span>Parametrising over class<aclass="headerlink" href="#parametrising-over-class" title="Link to this heading">¶</a></h3>
<p>When we create a class, there is always the possibility that someone will come
along later and create a subclass of it. It is therefore an important design
principle to avoid doing anything which might cause a problem in a subclass.
One important example of this is anywhere where it is assumed that the class of
<codeclass="xref py py-data docutils literal notranslate"><spanclass="pre">self</span></code> is in fact the current class and not some subclass of it. For this
reason, it is almost always a bad idea to explicitly use the name of the
current class inside its definition. Instead, we should use the fact that
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">type(self)</span></code> returns the type (i.e. class) of the current object. It is for
this reason that we typically use the formula <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">type(self).__name__</span></code> in the
<aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__repr__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__repr__()</span></code></a> method of an object. A similar procedure applies if we
need to create another object of the same class as the current object. For
example, one might create the next larger <aclass="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Group</span></code></a>
than the current one with:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">)(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">n</span><spanclass="o">+</span><spanclass="mi">1</span><spanclass="p">)</span>
</pre></div>
</div>
<p>Observe that since <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">type(self)</span></code> is a <aclass="reference internal" href="3_objects.html#term-class"><spanclass="xref std std-term">class</span></a>, we can <aclass="reference internal" href="3_objects.html#term-instantiate"><spanclass="xref std std-term">instantiate</span></a>
it by calling it.</p>
</section>
</section>
<sectionid="calling-parent-class-methods">
<h2><spanclass="section-number">7.3. </span>Calling parent class methods<aclass="headerlink" href="#calling-parent-class-methods" title="Link to this heading">¶</a></h2>
<divclass="literal-block-wrapper docutils container" id="id3">
<spanid="rectangle-class"></span><divclass="code-block-caption"><spanclass="caption-number">Listing 7.4 </span><spanclass="caption-text">The elementary rectangle class from <aclass="reference internal" href="example_code.html#module-example_code.shapes" title="example_code.shapes"><codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">example_code.shapes</span></code></a>.</span><aclass="headerlink" href="#id3" title="Link to this code">¶</a></div>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos"> 1</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">Rectangle</span><spanclass="p">:</span>
<spanclass="linenos"> 2</span>
<spanclass="linenos"> 3</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">length</span><spanclass="p">,</span><spanclass="n">width</span><spanclass="p">):</span>
<spanclass="linenos"> 4</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">length</span><spanclass="o">=</span><spanclass="n">length</span>
<spanclass="linenos"> 5</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">width</span><spanclass="o">=</span><spanclass="n">width</span>
<spanclass="linenos"> 6</span>
<spanclass="linenos"> 7</span><spanclass="k">def</span><spanclass="w"></span><spanclass="nf">area</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos"> 8</span><spanclass="k">return</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">length</span><spanclass="o">*</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">width</span>
<spanclass="linenos"> 9</span>
<spanclass="linenos">10</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__repr__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">11</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">)</span><spanclass="o">.</span><spanclass="vm">__name__</span><spanclass="si">}{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">length</span><spanclass="p">,</span><spanclass="w"></span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">width</span><spanclass="si">!r}</span><spanclass="s2">"</span>
</pre></div>
</div>
</div>
<p><aclass="reference internal" href="#rectangle-class"><spanclass="std std-numref">Listing 7.4</span></a> shows a basic implementation of a class describing a
rectangle. We might also want a class defining a square. Rather than redefining
everything from scratch, we might choose to <aclass="reference internal" href="#term-inheritance"><spanclass="xref std std-term">inherit</span></a> from
<aclass="reference internal" href="example_code.html#example_code.shapes.Rectangle" title="example_code.shapes.Rectangle"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Rectangle</span></code></a> by defining a square as a rectangle
whose length and width are equal. The <aclass="reference internal" href="3_objects.html#term-constructor"><spanclass="xref std std-term">constructor</span></a> for our new class
will, naturally, just take a single <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">length</span></code> parameter. However the
<aclass="reference internal" href="example_code.html#example_code.shapes.Rectangle.area" title="example_code.shapes.Rectangle.area"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">area()</span></code></a> method that we will inherit expects
both <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self.length</span></code> and <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self.width</span></code> to be defined. We could simply define both
length and width in <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">Square.__init__()</span></code>, but this is exactly the sort of
copy and paste code that inheritance is supposed to avoid. If the parameters to
<codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">Rectangle.__init__()</span></code> were to be changed at some future point, then having
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self.length</span></code> and <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self.width</span></code> defined in two separate places is likely to lead
to very confusing bugs.</p>
<p>Instead, we would like to have <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">Square.__init__()</span></code> call
<codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">Rectangle.__init__()</span></code> and pass the same value for both length and width. It
is perfectly possible to directly call <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">Rectangle.__init__()</span></code>, but this
breaks the style rule that we should not repeat ourselves: if <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Square</span></code>
already inherits from <aclass="reference internal" href="example_code.html#example_code.shapes.Rectangle" title="example_code.shapes.Rectangle"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Rectangle</span></code></a> then it should not
be necessary to restate that inheritance by explicitly naming the <aclass="reference internal" href="#term-parent-class"><spanclass="xref std std-term">parent
class</span></a>. Fortunately, Python provides the functionality we need in the form of
the <codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">super()</span></code> function. <aclass="reference internal" href="#square-class"><spanclass="std std-numref">Listing 7.5</span></a> demonstrates its application.</p>
<divclass="literal-block-wrapper docutils container" id="id4">
<spanid="square-class"></span><divclass="code-block-caption"><spanclass="caption-number">Listing 7.5 </span><spanclass="caption-text"><aclass="reference internal" href="example_code.html#example_code.shapes.Square" title="example_code.shapes.Square"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">example_code.shapes.Square</span></code></a> inherits from
<aclass="reference internal" href="example_code.html#example_code.shapes.Rectangle" title="example_code.shapes.Rectangle"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Rectangle</span></code></a> and calls the latter’s
<aclass="reference internal" href="3_objects.html#term-constructor"><spanclass="xref std std-term">constructor</span></a> using <codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">super()</span></code>.</span><aclass="headerlink" href="#id4" title="Link to this code">¶</a></div>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos">1</span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">Square</span><spanclass="p">(</span><spanclass="n">Rectangle</span><spanclass="p">):</span>
<spanclass="linenos">2</span>
<spanclass="linenos">3</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">length</span><spanclass="p">):</span>
<spanclass="linenos">4</span><spanclass="nb">super</span><spanclass="p">()</span><spanclass="o">.</span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="n">length</span><spanclass="p">,</span><spanclass="n">length</span><spanclass="p">)</span>
<spanclass="linenos">5</span>
<spanclass="linenos">6</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__repr__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos">7</span><spanclass="k">return</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">)</span><spanclass="o">.</span><spanclass="vm">__name__</span><spanclass="si">}</span><spanclass="s2">(</span><spanclass="si">{</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">length</span><spanclass="si">!r}</span><spanclass="s2">)"</span>
</pre></div>
</div>
</div>
<p>The <codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">super()</span></code> function returns a version of the current object in which none
of the <aclass="reference internal" href="3_objects.html#term-method"><spanclass="xref std std-term">methods</span></a> have been overridden by the current
<aclass="reference internal" href="3_objects.html#term-class"><spanclass="xref std std-term">class</span></a>. This has the effect that the <aclass="reference internal" href="#term-superclass"><spanclass="xref std std-term">superclasses</span></a> of
the current class are searched in increasing inheritance order until a matching
method name is found, and this method is then called. This provides a safe
mechanism for calling parent class methods in a way that responds appropriately
if someone later comes back and rewrites the inheritance relationships of the
classes involved.</p>
</section>
<sectionid="creating-new-exception-classes">
<spanid="defining-exceptions"></span><h2><spanclass="section-number">7.4. </span>Creating new exception classes<aclass="headerlink" href="#creating-new-exception-classes" title="Link to this heading">¶</a></h2>
<p>Python provides a wide range of <aclass="reference internal" href="6_exceptions.html#term-exception"><spanclass="xref std std-term">exceptions</span></a>, and usually the
right thing to do when writing code that might need to raise an exception is to
peruse the <aclass="reference external" href="https://docs.python.org/3/library/exceptions.html" title="(in Python v3.14)"><spanclass="xref std std-doc">list of built-in exceptions</span></a> and
choose the one which best matches the circumstances. However, sometimes there
is no good match, or it might be that the programmer wants user code to be able
to catch exactly this exception without the risk that some other operation will
raise the same exception and be caught by mistake. In this case, it is
necessary to create a new type of exception.</p>
<p>A new exception will be a new <aclass="reference internal" href="3_objects.html#term-class"><spanclass="xref std std-term">class</span></a> which inherits from another
exception class. In most cases, the only argument that the exception
<aclass="reference internal" href="3_objects.html#term-constructor"><spanclass="xref std std-term">constructor</span></a> takes is an error message, and the base <aclass="reference external" href="https://docs.python.org/3/library/exceptions.html#Exception" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Exception</span></code></a>
class already takes this. This means that the subclass definition may only need
to define the new class. Now, a class definition is a Python block and, as a
matter of <aclass="reference internal" href="6_exceptions.html#term-syntax"><spanclass="xref std std-term">syntax</span></a>, a block cannot be empty. Fortunately, the Python
language caters for this situation with the <aclass="reference external" href="https://docs.python.org/3/reference/simple_stmts.html#pass" title="(in Python v3.14)"><codeclass="xref std std-keyword docutils literal notranslate"><spanclass="pre">pass</span></code></a> statement, which
simply does nothing. For example, suppose we need to be able to distinguish the
<aclass="reference external" href="https://docs.python.org/3/library/exceptions.html#ValueError" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">ValueError</span></code></a> which occurs in entity validation from other occurrences of
<aclass="reference external" href="https://docs.python.org/3/library/exceptions.html#ValueError" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">ValueError</span></code></a>. For example it might be advantageous to enable a user to
catch exactly these errors. In this case, we’re still talking about some form
of value error, so we’ll want our new error class to inherit from
<aclass="reference external" href="https://docs.python.org/3/library/exceptions.html#ValueError" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">ValueError</span></code></a>. We could achieve this as follows:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">GroupValidationError</span><spanclass="p">(</span><spanclass="ne">ValueError</span><spanclass="p">):</span>
<spanclass="k">pass</span>
</pre></div>
</div>
</section>
<sectionid="glossary">
<h2><spanclass="section-number">7.5. </span>Glossary<aclass="headerlink" href="#glossary" title="Link to this heading">¶</a></h2>
<dlclass="simple glossary">
<dtid="term-child-class">child class<aclass="headerlink" href="#term-child-class" title="Link to this term">¶</a></dt><dd><p>A class which <aclass="reference internal" href="#term-inheritance"><spanclass="xref std std-term">inherits</span></a> directly from one or more
<aclass="reference internal" href="#term-parent-class"><spanclass="xref std std-term">parent classes</span></a>. The child class automatically has
all of the <aclass="reference internal" href="3_objects.html#term-method"><spanclass="xref std std-term">methods</span></a> of the parent classes, unless it
declares its own methods with the same names.</p>
</dd>
<dtid="term-class-attribute">class attribute<aclass="headerlink" href="#term-class-attribute" title="Link to this term">¶</a></dt><dd><p>An <aclass="reference internal" href="3_objects.html#term-attribute"><spanclass="xref std std-term">attribute</span></a> which is declared directly on a <aclass="reference internal" href="3_objects.html#term-class"><spanclass="xref std std-term">class</span></a>.
All instances of a class see the same value of a class attribute.</p>
</dd>
<dtid="term-composition">composition<aclass="headerlink" href="#term-composition" title="Link to this term">¶</a></dt><dd><p>The process of making a more complex object from other objects by
including the constituent objects as attributes of the more composite
object. Composition can be characterised as a <em>has a</em> relationship, in
contrast to <aclass="reference internal" href="#term-inheritance"><spanclass="xref std std-term">inheritance</span></a>, which embodies an <em>is a</em> relationship.</p>
</dd>
<dtid="term-delegation">delegation<aclass="headerlink" href="#term-delegation" title="Link to this term">¶</a></dt><dd><p>A design pattern in which an object avoids implementing a
<aclass="reference internal" href="3_objects.html#term-method"><spanclass="xref std std-term">method</span></a> by instead calling a method on another object.</p>
</dd>
<dtid="term-inheritance">inheritance<aclass="headerlink" href="#term-inheritance" title="Link to this term">¶</a></dt><dd><p>The process of making a new class by extending or modifying one or more existing
classes.</p>
</dd>
<dtid="term-parent-class">parent class<aclass="headerlink" href="#term-parent-class" title="Link to this term">¶</a></dt><dd><p>A class from which another class, referred to as a <aclass="reference internal" href="#term-child-class"><spanclass="xref std std-term">child class</span></a>,
inherits. Inheritance can be characterised as an <em>is a</em> relationship, in
contrast to <aclass="reference internal" href="#term-composition"><spanclass="xref std std-term">composition</span></a>, which embodies an <em>has a</em> relationship.</p>
</dd>
<dtid="term-subclass">subclass<aclass="headerlink" href="#term-subclass" title="Link to this term">¶</a></dt><dd><p>A class <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">A</span></code> is a subclass of the class <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">B</span></code> if <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">A</span></code> inherits from <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">B</span></code> either
directly or indirectly. That is, if <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">B</span></code> is a <aclass="reference internal" href="#term-parent-class"><spanclass="xref std std-term">parent</span></a>,
grandparent, great grandparent or further ancestor of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">A</span></code>. Contrast
<aclass="reference internal" href="#term-superclass"><spanclass="xref std std-term">superclass</span></a>.</p>
</dd>
<dtid="term-superclass">superclass<aclass="headerlink" href="#term-superclass" title="Link to this term">¶</a></dt><dd><p>A class <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">A</span></code> is a superclass of the class <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">B</span></code> if <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">B</span></code> inherits from <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">A</span></code> either
directly or indirectly. That is, if <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">B</span></code> is a <aclass="reference internal" href="#term-subclass"><spanclass="xref std std-term">subclass</span></a> of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">A</span></code>.</p>
</dd>
</dl>
</section>
<sectionid="exercises">
<h2><spanclass="section-number">7.6. </span>Exercises<aclass="headerlink" href="#exercises" title="Link to this heading">¶</a></h2>
<p>Using the information on the <aclass="reference external" href="https://object-oriented-python.github.io/edition3/exercises.html">book website</a>
obtain the skeleton code for these exercises.</p>
<divclass="proof proof-type-exercise" id="id5">
<divclass="proof-title">
<spanclass="proof-type">Exercise 7.1</span>
</div><divclass="proof-content">
<p>The symmetric group over <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">n</span></code> symbols is the group whose members are all the
permutations of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">n</span></code> symbols and whose group operation is the composition of
those permutations: <spanclass="math notranslate nohighlight">\(a \cdot b = a(b)\)</span>.</p>
<p>In the exercise repository, create package called
<codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">groups</span></code> containing a module called <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">symmetric_groups</span></code>. Define a
new class <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">SymmetricGroup</span></code> which inherits from
<aclass="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">example_code.groups.Group</span></code></a> and implements the symmetric group of
order <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">n</span></code>. You will need to implement the group operation and the
validation of group element values. Group elements can be represented by
sequences containing permutations of the integers from 0 to <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">n-1</span></code>. You will
find it advantageous to represent these permutations as
<aclass="reference external" href="https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html#numpy.ndarray" title="(in NumPy v2.3)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">numpy.ndarray</span></code></a> because the indexing rules for that type mean that
the group operation can simply be implemented by indexing the first
permutation with the second: <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a[b]</span></code>.</p>
<p>You will also need to set the <aclass="reference internal" href="#term-class-attribute"><spanclass="xref std std-term">class attribute</span></a><codeclass="xref py py-attr docutils literal notranslate"><spanclass="pre">symbol</span></code>. For this
group, this should take the value <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">S</span></code>.</p>
<divclass="admonition hint">
<pclass="admonition-title">Hint</p>
<p>You will need to <aclass="reference external" href="https://docs.python.org/3/reference/simple_stmts.html#import" title="(in Python v3.14)"><codeclass="xref std std-keyword docutils literal notranslate"><spanclass="pre">import</span></code></a><aclass="reference internal" href="example_code.html#example_code.groups.Group" title="example_code.groups.Group"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">example_code.groups.Group</span></code></a>
from the <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">object_oriented_programming</span></code> repository that you installed in
<aclass="reference internal" href="2_programs_in_files.html#programs-in-files-exercises"><spanclass="std std-numref">Section 2.8</span></a>. You should also <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">git</span><spanclass="pre">pull</span></code> in
that repository in order to get any changes that have happened in the
intervening period.</p>
</div>
<divclass="admonition hint">
<pclass="admonition-title">Hint</p>
<p>In implementing element validation, the builtin function <aclass="reference external" href="https://docs.python.org/3/library/functions.html#sorted" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">sorted()</span></code></a>
is likely to be useful.</p>
</div>
</div></div><divclass="proof proof-type-exercise" id="id6">
<divclass="proof-title">
<spanclass="proof-type">Exercise 7.2</span>
</div><divclass="proof-content">
<p>The objective of this exercise is to create subclasses of the built-in
<aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#set" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">set</span></code></a> class which are only valid for values which pass a certain
test. For example, one might have a set which can only contain integers.</p>
<olclass="arabic simple">
<li><p>In the exercise repository, create a package called <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">sets</span></code> containing a
module <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">verified_sets</span></code>. Create a subclass of the inbuilt <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#set" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">set</span></code></a>,
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">sets.verified_sets.VerifiedSet</span></code>. <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code> will itself
be the parent of other classes which have particular verification rules.</p></li>
<li><p>Give <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code> a method <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">_verify()</span></code> which takes a single
value. In the case of <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code>, <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">_verify()</span></code> should
unconditionally raise <aclass="reference external" href="https://docs.python.org/3/library/exceptions.html#NotImplementedError" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">NotImplementedError</span></code></a>. Subclasses of
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code> will override this method to do something more useful.</p></li>
<li><p>For each <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#set" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">set</span></code></a> method which adds items to the set,
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code> will need to have its own version which calls
<codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">_verify()</span></code> on each item, before calling the appropriate superclass
method in order to actually insert the value(s). The methods which add
items to a set are <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">add()</span></code>, <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">update()</span></code>, and
<codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">symmetric_difference_update()</span></code>.</p></li>
<li><p>For those methods which create a new set, <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code> will also
need to <aclass="reference internal" href="3_objects.html#term-instantiate"><spanclass="xref std std-term">instantiate</span></a> a new object, so that the method returns a subclass of
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code> instead of a plain <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#set" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">set</span></code></a>. The methods to which
this applies are <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#frozenset.union" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">union()</span></code></a>, <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#frozenset.intersection" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">intersection()</span></code></a>,
<aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#frozenset.difference" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">difference()</span></code></a>, <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#frozenset.symmetric_difference" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">symmetric_difference()</span></code></a>, and
<aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#frozenset.copy" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">copy()</span></code></a>.</p></li>
<li><p>Create a subclass of <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code> called <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">IntSet</span></code> in which
only integers (i.e. instances of <aclass="reference external" href="https://docs.python.org/3/library/numbers.html#numbers.Integral" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">numbers.Integral</span></code></a>) are allowed.
On encountering a non-integer <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">IntSet._verify()</span></code> should raise
<aclass="reference external" href="https://docs.python.org/3/library/exceptions.html#TypeError" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">TypeError</span></code></a> with an error message of the following form. For example
if an attempt were made to add a string to the set, the message would be
“IntSet expected an integer, got a str.”.</p></li>
<li><p>Create a subclass of <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">VerifiedSet</span></code> called <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">UniqueSet</span></code> into
which values can only be added if they are not already in the set. You
should create a new exception <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">UniquenessError</span></code>, a subclass of
<aclass="reference external" href="https://docs.python.org/3/library/exceptions.html#KeyError" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">KeyError</span></code></a>. <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">UniqueSet._verify</span></code> should raise this if an
operation would add a duplicate value to the <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">UniqueSet</span></code>.</p></li>
</ol>
</div></div><pclass="rubric">Footnotes</p>
<asideclass="footnote-list brackets">
<asideclass="footnote brackets" id="exercise-page" role="doc-footnote">
<spanclass="label"><spanclass="fn-bracket">[</span><arole="doc-backlink" href="#id2">1</a><spanclass="fn-bracket">]</span></span>
<p><aclass="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>
<divclass="clearer"></div>
</div>
</div>
</div>
<divclass="clearer"></div>
</div>
<divclass="footer" role="contentinfo">
© Copyright 2019-2023, David A. Ham.
Created using <ahref="https://www.sphinx-doc.org/">Sphinx</a> 7.4.7.
</div>
</body>
</html>