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 path3_objects.html
More file actions
Latest commit
980 lines (916 loc) · 108 KB
/
Copy path3_objects.html
File metadata and controls
980 lines (916 loc) · 108 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
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
<!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>3. Objects and abstraction — 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="4. A matter of style" href="4_style.html" />
<linkrel="prev" title="2. Programs in files" href="2_programs_in_files.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="objects-and-abstraction">
<spanid="objects"></span><h1><spanclass="section-number">3. </span>Objects and abstraction<aclass="headerlink" href="#objects-and-abstraction" title="Link to this heading">¶</a></h1>
<p>In this chapter we will take a first look at the representation of
abstract mathematical objects and operations as data objects in a
computer program. We will learn about what it means for objects to have
a <aclass="reference internal" href="#term-type"><spanclass="xref std std-term">type</span></a>, and how to create new types using the <aclass="reference external" href="https://docs.python.org/3/reference/compound_stmts.html#class" title="(in Python v3.14)"><codeclass="xref std std-keyword docutils literal notranslate"><spanclass="pre">class</span></code></a> keyword.</p>
<sectionid="abstraction-in-action">
<h2><spanclass="section-number">3.1. </span>Abstraction in action<aclass="headerlink" href="#abstraction-in-action" title="Link to this heading">¶</a></h2>
<p>Consider this line of Python code:</p>
<divclass="highlight-default notranslate"><divclass="highlight"><pre><span></span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
</pre></div>
</div>
<p>What does it do? Well, assuming that <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span></code> and <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">b</span></code> are suitably defined, it
prints their sum. This, however, begs the questions: what is “suitably
defined”, and what is “sum”? For example:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="mi">1</span>
<spanclass="gp">In [2]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="mi">2</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="go">3</span>
</pre></div>
</div>
<p>You’re unlikely to be surprised that Python can add <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#typesnumeric" title="(in Python v3.14)"><spanclass="xref std std-ref">integers</span></a>. On the other hand it turns out we can also add strings:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="s1">'fr'</span>
<spanclass="gp">In [2]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="s1">'og'</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="go">'frog'</span>
</pre></div>
</div>
<p>So the meaning of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">+</span></code> depends on what is being added. What happens if
we add an integer to a <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#textseq" title="(in Python v3.14)"><spanclass="xref std std-ref">string</span></a>?</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="mi">1</span>
<spanclass="gp">In [2]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="s1">'og'</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="gt">--------------------------------------------------------------------------</span>
<spanclass="ne">TypeError</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">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="ne">TypeError</span>: unsupported operand type(s) for +: 'int' and 'str'
</pre></div>
</div>
<p>In this error, Python is complaining that <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">+</span></code> does not make sense if
the items being added (the <aclass="reference internal" href="#term-operands"><spanclass="xref std std-term">operands</span></a>) are an integer and a
string. This makes our understanding of “suitably defined” more
concrete: clearly some pairs of objects can be added and others
can’t. However, we should be careful about the conclusions we draw. We
might be tempted to believe that we can add two values if they are of
the same type. However, if we try this with a pair of <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#types-set" title="(in Python v3.14)"><spanclass="xref std std-ref">sets</span></a> then we’re
also in trouble:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="p">{</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">}</span>
<spanclass="gp">In [2]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="p">{</span><spanclass="mi">2</span><spanclass="p">,</span><spanclass="mi">3</span><spanclass="p">}</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="gt">--------------------------------------------------------------------------</span>
<spanclass="ne">TypeError</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">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="ne">TypeError</span>: unsupported operand type(s) for +: 'set' and 'set'
</pre></div>
</div>
<p>Conversely we might suspect that two values can be added only if they are of the same
type. However it is perfectly legal to add an integer and a <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#typesnumeric" title="(in Python v3.14)"><spanclass="xref std std-ref">floating
point value</span></a>:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="mi">1</span>
<spanclass="gp">In [2]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="mf">2.5</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="go">3.5</span>
</pre></div>
</div>
<p>In Python, the operator <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">+</span></code> encodes an abstraction for addition. This means
that <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">+</span></code> stands for the addition operation, whatever that may mean for
a particular pair of operands. For the purposes of the abstraction,
everything which is specific to the particular operands is
ignored. This includes, for example,
the mechanism by which the addition is calculated and the value of the
result. This enables a programmer to think about the relatively simple
mathematical operation of addition, rather than the potentially
complex or messy way it might be implemented for particular data.</p>
<divclass="proof proof-type-definition" id="id4">
<divclass="proof-title">
<spanclass="proof-type">Definition 3.1</span>
</div><divclass="proof-content">
<p>An <em>abstraction</em> is a mathematical object with a limited set of
defined properties. For the purposes of the abstraction, any other
properties that an object may have are disregarded.</p>
</div></div><p>An abstraction is a purely mathematical concept, but it is one which
maps to one or more concrete realisations in code. Sometimes the
abstract mathematical concept and its concrete realisation match so
perfectly that it is difficult to distinguish the two. In those
circumstances, we usually conflate the terminology for the abstraction
and the code object. “Type” is one such example, and we turn to that
now.</p>
</section>
<sectionid="types">
<h2><spanclass="section-number">3.2. </span>Types<aclass="headerlink" href="#types" title="Link to this heading">¶</a></h2>
<p>In the previous section, we observed that addition may or may not be
defined, depending on what the types of its operands are. In doing so,
we skirted the question of what it means for an object to have
type.</p>
<divclass="proof proof-type-definition" id="id5">
<divclass="proof-title">
<spanclass="proof-type">Definition 3.2</span>
</div><divclass="proof-content">
<p>A <em>type</em> or <em>class</em> is an abstraction defined by a set of possible values, and
a set of operators valid for objects of that type.</p>
</div></div><p>Every object in Python has a type. This is true for primitive numeric
types, such as <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>, <aclass="reference external" href="https://docs.python.org/3/library/functions.html#float" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">float</span></code></a>, and <aclass="reference external" href="https://docs.python.org/3/library/functions.html#complex" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">complex</span></code></a>; for sequences such as
string (<aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#str" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">str</span></code></a>), <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>, and <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#list" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">list</span></code></a>; and also for more complex types
such as <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> and dictionary (<aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#dict" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">dict</span></code></a>). Indeed, the
Python concept of type goes much further, as we discover if we call
<aclass="reference external" href="https://docs.python.org/3/library/functions.html#type" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">type</span></code></a> on various objects:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">)</span>
<spanclass="gh">Out[1]: </span><spanclass="go">int</span>
<spanclass="gp">In [2]: </span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="nb">abs</span><spanclass="p">)</span>
<spanclass="gh">Out[2]: </span><spanclass="go">builtin_function_or_method</span>
</pre></div>
</div>
<p>So <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">1</span></code> is an object of type <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>, which means that it comes with all of
Python’s operations for integer arithmetic. <aclass="reference external" href="https://docs.python.org/3/library/functions.html#abs" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">abs()</span></code></a>, on the other hand, is
a <aclass="reference external" href="https://docs.python.org/3/library/functions.html" title="(in Python v3.14)"><spanclass="xref std std-doc">built-in function</span></a>, so its defining
operation is that it can be called on one or more suitable arguments (for
example <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">abs(1)</span></code>). If every object has a type, what about types themselves?
What is the type of <aclass="reference external" href="https://docs.python.org/3/library/functions.html#int" title="(in Python v3.14)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">int</span></code></a>?</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="nb">int</span><spanclass="p">)</span>
<spanclass="gh">Out[1]: </span><spanclass="go">type</span>
</pre></div>
</div>
<p>We see 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 the type of integer objects, and is itself an
object with type <aclass="reference external" href="https://docs.python.org/3/library/functions.html#type" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">type</span></code></a>. That rather invites the question of what
is the type of <aclass="reference external" href="https://docs.python.org/3/library/functions.html#type" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">type</span></code></a>?</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="nb">type</span><spanclass="p">)</span>
<spanclass="gh">Out[1]: </span><spanclass="go">type</span>
</pre></div>
</div>
<p>This actually makes perfect sense, because <aclass="reference external" href="https://docs.python.org/3/library/functions.html#type" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">type</span></code></a> is simply the
type of types.</p>
<p>We will return to types in much more detail later. At this stage, the
take-home message is that essentially everything you will encounter in
Python is an object, and every object has a type.</p>
<divclass="admonition note">
<pclass="admonition-title">Note</p>
<p>In Python, the term
“class” is essentially synonymous with “type”, so “what is the class
of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">foo</span></code>” is the same as saying “what is the type of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">foo</span></code>”. However
the two terms are not synonyms when used in code. <aclass="reference external" href="https://docs.python.org/3/library/functions.html#type" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">type</span></code></a> can be
used to determine the type of an object, while <aclass="reference external" href="https://docs.python.org/3/reference/compound_stmts.html#class" title="(in Python v3.14)"><codeclass="xref std std-keyword docutils literal notranslate"><spanclass="pre">class</span></code></a> is
used to define new types.</p>
</div>
</section>
<sectionid="defining-new-types">
<h2><spanclass="section-number">3.3. </span>Defining new types<aclass="headerlink" href="#defining-new-types" title="Link to this heading">¶</a></h2>
<details>
<summary>
Video: a first class</summary><divclass="video_wrapper" style="">
<iframeallowfullscreen="true" src="https://player.vimeo.com/video/488143930" 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=b70b944d-612c-4e57-9538-af8d01031342">watch this video on Panopto</a></p>
</details><p>Python has a rich set of <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html" title="(in Python v3.14)"><spanclass="xref std std-doc">built-in types</span></a>. These form powerful building blocks for the
language, but one very soon finds mathematical abstractions which do
not have implementations among the built-in types of the Python
interpreter. For example, the built-in types do not include a matrix
or multidimensional array type. The ability to make new data types
which provide concrete implementations of further mathematical
abstractions is central to effectively exploiting abstraction in
programming.</p>
<p>As an example, lets suppose that we want to work with real polynomials in
one variable. That is to say, functions of the form:</p>
<divclass="math notranslate nohighlight">
\[f(x) = \sum_{n=0}^d c_n x^n \quad \textrm{for some } d\in
\mathbb{N}, c_n \in \mathbb{R}\]</div>
<p>The set of all polynomials is a well-defined (though infinite) set of
different values, with a number of well-defined properties. For
example, we can add and multiply polynomials, resulting in a new
polynomial. We can also evaluate a polynomial for a particular value
of <spanclass="math notranslate nohighlight">\(x\)</span>, which would result in a real value.</p>
<p>This is the mathematical abstraction of a polynomial. How would we
represent this abstraction in Python code? A polynomial is
characterised by its set of coefficients, so we could in principle
represent a polynomial as a <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 coefficient
values. However, the addition of tuples is <aclass="reference internal" href="#term-concatenation"><spanclass="xref std std-term">concatenation</span></a>, and
multiplication of two tuples isn’t even defined, so this would be a
very poor representation of the mathematics: a polynomial represented
as a tuple of coefficients would not behave the way a mathematician
would expect. Instead, what we need to do is make a new type whose
operations match the mathematical properties of a polynomial.</p>
<sectionid="classes-and-constructors">
<h3><spanclass="section-number">3.3.1. </span>Classes and constructors<aclass="headerlink" href="#classes-and-constructors" title="Link to this heading">¶</a></h3>
<p>The Python keyword for declaring a new type is
<aclass="reference external" href="https://docs.python.org/3/reference/compound_stmts.html#class" title="(in Python v3.14)"><codeclass="xref std std-keyword docutils literal notranslate"><spanclass="pre">class</span></code></a>. Just like a function declaration, this creates a new
indented block. In this case, the block contains all of the function
declarations which define the operations on this new type. Let’s make
a very simple implementation:</p>
<divclass="highlight-default notranslate"><divclass="highlight"><pre><span></span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">Polynomial</span><spanclass="p">:</span>
<spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">coefs</span><spanclass="p">):</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="o">=</span><spanclass="n">coefs</span>
</pre></div>
</div>
<p>We’ll interpret the <spanclass="math notranslate nohighlight">\(i\)</span>-th coefficient as the coefficient of <spanclass="math notranslate nohighlight">\(x^i\)</span>.
This will simplify the program logic, but take care because mathematicians
usually write coefficients from largest power of <spanclass="math notranslate nohighlight">\(x\)</span> to smallest, and this
is the opposite of that. Executing this code in a Python interpreter would enable us to create
a simple polynomial, and inspect its coefficients:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [7]: </span><spanclass="n">f</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">))</span>
<spanclass="gp">In [8]: </span><spanclass="n">f</span><spanclass="o">.</span><spanclass="n">coefficients</span>
<spanclass="gh">Out[8]: </span><spanclass="go">(0, 1, 2)</span>
</pre></div>
</div>
<p>The three lines of Python defining the <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> class contain
several important concepts and Python details that it is important to
understand.</p>
<p>The <aclass="reference external" href="https://docs.python.org/3/reference/compound_stmts.html#class" title="(in Python v3.14)"><spanclass="xref std std-ref">class definition</span></a> statement opens a new block, so
just like a <aclass="reference external" href="https://docs.python.org/3/reference/compound_stmts.html#function" title="(in Python v3.14)"><spanclass="xref std std-ref">function definition</span></a>, it starts with
the keyword followed by the name of the class we are defining, and
ends with a colon. User-defined classes in Python (i.e. classes not
built into the language) usually have CapWords names. This means
that all the words in the name are capitalised and run together without spaces. For
example, if we decided to make a separate class for complex-valued
polynomials, we might call it <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">ComplexPolynomial</span></code>.</p>
<p>Inside the class definition, i.e. indented inside the block, is a
function called <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a>. Functions defined inside a class
definition are called <aclass="reference internal" href="#term-method"><spanclass="xref std std-term">methods</span></a>. The <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a> method has a
rather distinctive form of name, starting and ending with two
underscores. Names of this format are used in the Python language for
objects which have special meaning in the Python language. The
<aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a> method of a class has special meaning in Python as
the <aclass="reference internal" href="#term-constructor"><spanclass="xref std std-term">constructor</span></a> of a class. When we write:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [7]: </span><spanclass="n">f</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">))</span>
</pre></div>
</div>
<p>This is called <aclass="reference internal" href="#term-instantiate"><spanclass="xref std std-term">instantiating</span></a> an object of type
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>. The following steps occur:</p>
<olclass="arabic simple">
<li><p>Python creates an object of type <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>.</p></li>
<li><p>The <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a><aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special method</span></a> of <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>
is called. The new <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> object is passed as the
first parameter (<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code>), and the <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><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">(0,</span><spanclass="pre">1,</span><spanclass="pre">2)</span></code> is passed
as the second parameter (<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">coefs</span></code>).</p></li>
<li><p>The name <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">f</span></code> in the surrounding <aclass="reference internal" href="2_programs_in_files.html#term-scope"><spanclass="xref std std-term">scope</span></a> is associated with the
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>.</p></li>
</ol>
<divclass="admonition note">
<pclass="admonition-title">Note</p>
<p>Notice that <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">Polynomial.__init__()</span></code> doesn’t return anything. The role
of the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a> method is to set up the object, <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code>; it
is not to return a value. <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a> never returns a value.</p>
</div>
</section>
<sectionid="attributes">
<h3><spanclass="section-number">3.3.2. </span>Attributes<aclass="headerlink" href="#attributes" title="Link to this heading">¶</a></h3>
<p>Let’s now look at what happened inside the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a> method. We
have just one line:</p>
<divclass="highlight-default notranslate"><divclass="highlight"><pre><span></span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="o">=</span><spanclass="n">coefs</span>
</pre></div>
</div>
<p>Remember that <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code> is the object we are setting up, and <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">coefs</span></code> is the
other parameter to <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a>. This line of code creates a new
name inside this <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> object, called
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">coefficients</span></code>, and associates this new name with the object passed as
the argument to the <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> constructor. Names such as
this are called <aclass="reference internal" href="#term-attribute"><spanclass="xref std std-term">attributes</span></a>. We create an attribute
just by assigning to it, and we can then read back the attribute using
the same syntax, which is what we did here:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [8]: </span><spanclass="n">f</span><spanclass="o">.</span><spanclass="n">coefficients</span>
<spanclass="gh">Out[8]: </span><spanclass="go">(0, 1, 2)</span>
</pre></div>
</div>
<p>Attributes can be given any name which is allowed for a Python name in general -
which is to say sequences of letters, numbers and underscores starting with a
letter or an underscore. Special significance attaches to names starting with an
underscore, so these should be avoided in your own names unless you intend to
create a private attribute.</p>
</section>
<sectionid="methods">
<h3><spanclass="section-number">3.3.3. </span>Methods<aclass="headerlink" href="#methods" title="Link to this heading">¶</a></h3>
<details>
<summary>
Video: defining methods</summary><divclass="video_wrapper" style="">
<iframeallowfullscreen="true" src="https://player.vimeo.com/video/488273256" 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=b3a56e5f-2ed4-4417-aaf8-ae1c00da6017">watch this video on Panopto</a></p>
</details><p>We have already met the <aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special method</span></a><aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a>,
which defines the class constructor. A much more typical case is an
ordinary method, without a special underscore name. For example,
suppose we wish to be able to access the degree of a polynomial, then
we might add a <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">degree()</span></code> method to our class:</p>
<divclass="highlight-default notranslate"><divclass="highlight"><pre><span></span><spanclass="k">class</span><spanclass="w"></span><spanclass="nc">Polynomial</span><spanclass="p">:</span>
<spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__init__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">coefs</span><spanclass="p">):</span>
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="o">=</span><spanclass="n">coefs</span>
<spanclass="k">def</span><spanclass="w"></span><spanclass="nf">degree</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="k">return</span><spanclass="nb">len</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="p">)</span><spanclass="o">-</span><spanclass="mi">1</span>
</pre></div>
</div>
<p>Observe that the new method is indented inside the <aclass="reference external" href="https://docs.python.org/3/reference/compound_stmts.html#class" title="(in Python v3.14)"><codeclass="xref std std-keyword docutils literal notranslate"><spanclass="pre">class</span></code></a>
block at the same level as the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a> method. Observe also
that it too takes <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code> as its first parameter. A key difference from
the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a> method is that <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">degree()</span></code> now returns a
value, as most functions do. We can now use our new method to recover
the degree of our polynomial.</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">f</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">))</span>
<spanclass="gp">In [2]: </span><spanclass="n">f</span><spanclass="o">.</span><spanclass="n">degree</span><spanclass="p">()</span>
<spanclass="gh">Out[2]: </span><spanclass="go">2</span>
</pre></div>
</div>
<p>To clarify the role of the <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code> parameter it helps to understand
that <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">f.degree()</span></code> is just a short way of writing
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Polynomial.degree(f)</span></code>. Like attributes, methods can have any allowed Python
name. Attributes and methods on an object form part of the same
<aclass="reference internal" href="2_programs_in_files.html#term-namespace"><spanclass="xref std std-term">namespace</span></a>, so you can’t have an attribute and a method with the same
name. If you try, then the name will be overwritten with whichever was defined
later, and the attribute or method defined first will no longer be accessible
under that name. This is unlikely to be what you wanted.</p>
<divclass="admonition note">
<pclass="admonition-title">Note</p>
<p>The object itself is always passed as the first argument to a <aclass="reference internal" href="#term-method"><spanclass="xref std std-term">method</span></a>.
Technically, it is possible to name the first parameter any legal Python
name, but there is a <strong>very</strong> strong convention that the first parameter to
any <aclass="reference internal" href="#term-instance-method"><spanclass="xref std std-term">instance method</span></a> is called <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code>. <strong>Never, ever</strong> name this
parameter anything other than <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code>, or you will confuse every Python
programmer who reads your code!</p>
</div>
</section>
<sectionid="string-representations-of-objects">
<h3><spanclass="section-number">3.3.4. </span>String representations of objects<aclass="headerlink" href="#string-representations-of-objects" title="Link to this heading">¶</a></h3>
<details>
<summary>
Video: printing classes</summary><divclass="video_wrapper" style="">
<iframeallowfullscreen="true" src="https://player.vimeo.com/video/488275072" 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=4fe0252a-619d-4959-99f0-ae1c00da657d">watch this video on Panopto</a></p>
</details><p>Remember that a key reason for defining new classes is to enable users
to reason about the resulting objects at a higher mathematical level. An
important aid to the user in doing this is to be able to look at the
object. What happens if we print a <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>?</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">f</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">))</span>
<spanclass="gp">In [2]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">f</span><spanclass="p">)</span>
<spanclass="go"><Polynomial object at 0x104960dd0></span>
</pre></div>
</div>
<p>This is less than useful. By default, Python just prints the class of
the object and the memory address at which this particular object is
stored. This is, however, not so surprising if we think about the
situation in a little more depth. How was Python supposed to know what
sort of string representation makes sense for this object? We will
have to tell it.</p>
<p>The way we do so is using another <aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special method</span></a>. The special
method name for the human readable string representation of an object is
<aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__str__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__str__()</span></code></a>. It takes no arguments other than the object itself.
<aclass="reference internal" href="#polynomial-str"><spanclass="std std-numref">Listing 3.1</span></a> provides one possible implementation of this method.</p>
<divclass="literal-block-wrapper docutils container" id="polynomial-str">
<divclass="code-block-caption"><spanclass="caption-number">Listing 3.1 </span><spanclass="caption-text">An implementation of the string representation of a
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>. This takes into account the usual conventions for
writing polynomials, including writing the highest degree terms first, and
omitting zero terms and unit coefficients.</span><aclass="headerlink" href="#polynomial-str" title="Link to this code">¶</a></div>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos"> 1</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__str__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="linenos"> 2</span>
<spanclass="linenos"> 3</span><spanclass="n">coefs</span><spanclass="o">=</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span>
<spanclass="linenos"> 4</span><spanclass="n">terms</span><spanclass="o">=</span><spanclass="p">[]</span>
<spanclass="linenos"> 5</span>
<spanclass="linenos"> 6</span><spanclass="c1"># Degree 0 and 1 terms conventionally have different representation.</span>
<spanclass="linenos"> 7</span><spanclass="k">if</span><spanclass="n">coefs</span><spanclass="p">[</span><spanclass="mi">0</span><spanclass="p">]:</span>
<spanclass="linenos"> 8</span><spanclass="n">terms</span><spanclass="o">.</span><spanclass="n">append</span><spanclass="p">(</span><spanclass="nb">str</span><spanclass="p">(</span><spanclass="n">coefs</span><spanclass="p">[</span><spanclass="mi">0</span><spanclass="p">]))</span>
<spanclass="linenos"> 9</span><spanclass="k">if</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">degree</span><spanclass="p">()</span><spanclass="o">></span><spanclass="mi">0</span><spanclass="ow">and</span><spanclass="n">coefs</span><spanclass="p">[</span><spanclass="mi">1</span><spanclass="p">]:</span>
<spanclass="linenos">10</span><spanclass="n">terms</span><spanclass="o">.</span><spanclass="n">append</span><spanclass="p">(</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="n">coefs</span><spanclass="p">[</span><spanclass="mi">1</span><spanclass="p">]</span><spanclass="si">}</span><spanclass="s2">x"</span><spanclass="p">)</span>
<spanclass="linenos">11</span>
<spanclass="linenos">12</span><spanclass="c1"># Remaining terms look like cx^d, though factors of 1 are dropped.</span>
<spanclass="linenos">13</span><spanclass="n">terms</span><spanclass="o">+=</span><spanclass="p">[</span><spanclass="sa">f</span><spanclass="s2">"</span><spanclass="si">{</span><spanclass="s1">''</span><spanclass="w"></span><spanclass="k">if</span><spanclass="w"></span><spanclass="n">c</span><spanclass="w"></span><spanclass="o">==</span><spanclass="w"></span><spanclass="mi">1</span><spanclass="w"></span><spanclass="k">else</span><spanclass="w"></span><spanclass="n">c</span><spanclass="si">}</span><spanclass="s2">x^</span><spanclass="si">{</span><spanclass="n">d</span><spanclass="si">}</span><spanclass="s2">"</span>
<spanclass="linenos">14</span><spanclass="k">for</span><spanclass="n">d</span><spanclass="p">,</span><spanclass="n">c</span><spanclass="ow">in</span><spanclass="nb">enumerate</span><spanclass="p">(</span><spanclass="n">coefs</span><spanclass="p">[</span><spanclass="mi">2</span><spanclass="p">:],</span><spanclass="n">start</span><spanclass="o">=</span><spanclass="mi">2</span><spanclass="p">)</span><spanclass="k">if</span><spanclass="n">c</span><spanclass="p">]</span>
<spanclass="linenos">15</span>
<spanclass="linenos">16</span><spanclass="c1"># Sum polynomial terms from high to low exponent.</span>
<spanclass="linenos">17</span><spanclass="k">return</span><spanclass="s2">" + "</span><spanclass="o">.</span><spanclass="n">join</span><spanclass="p">(</span><spanclass="nb">reversed</span><spanclass="p">(</span><spanclass="n">terms</span><spanclass="p">))</span><spanclass="ow">or</span><spanclass="s2">"0"</span>
</pre></div>
</div>
</div>
<p>This somewhat longer piece of code results from the fact that the
linear and constant terms in a polynomial are usually represented
slightly differently from the higher-order terms. Having added this
new method to our class, we can now observe the result:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [2]: </span><spanclass="n">f</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">,</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</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">f</span><spanclass="p">)</span>
<spanclass="go">5x^4 + x^3 + 2x + 1</span>
</pre></div>
</div>
<p>In fact, Python provides not one, but two <aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special
methods</span></a> which convert an object to a
string. <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__str__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__str__()</span></code></a> is called by <aclass="reference external" href="https://docs.python.org/3/library/functions.html#print" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">print()</span></code></a> and also by
<aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#str" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">str</span></code></a>. Its role is to provide the string representation which
is best understood by humans. In mathematical code, this will usually
be the mathematical notation for the object. In contrast, 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 is called by <aclass="reference external" href="https://docs.python.org/3/library/functions.html#repr" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">repr()</span></code></a> and also provides
the default string representation printed out by the Python command
line. By convention, <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> should return a string which a
user might type in order to recreate the object. For example:</p>
<divclass="highlight-default notranslate"><divclass="highlight"><pre><span></span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__repr__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</span>
<spanclass="k">return</span><spanclass="nb">type</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">)</span><spanclass="o">.</span><spanclass="vm">__name__</span><spanclass="o">+</span><spanclass="s2">"("</span><spanclass="o">+</span><spanclass="nb">repr</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="p">)</span><spanclass="o">+</span><spanclass="s2">")"</span>
</pre></div>
</div>
<p><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">type(self).__name__</span></code> simply evaluates to the class name, in this case
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Polynomial</span></code>. This is better than hard-coding the class name because, as we will
see in <aclass="reference internal" href="7_inheritance.html#inheritance"><spanclass="std std-numref">Chapter 7</span></a>, this implementation of
<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> might well end up being inherited by a class with a
different name. Notice that in order to help ensure consistency of
representations we call <aclass="reference external" href="https://docs.python.org/3/library/functions.html#repr" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">repr()</span></code></a> on the coefficients in this case, whereas
in the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__str__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__str__()</span></code></a> method we called <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#str" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">str</span></code></a>.</p>
<p>We can now observe the difference in the result:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [2]: </span><spanclass="n">f</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">,</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">4</span><spanclass="p">,</span><spanclass="mi">5</span><spanclass="p">))</span>
<spanclass="gp">In [3]: </span><spanclass="n">f</span>
<spanclass="gh">Out[3]: </span><spanclass="go">Polynomial((1, 2, 0, 4, 5))</span>
</pre></div>
</div>
<p>When using <aclass="reference external" href="https://docs.python.org/3/tutorial/inputoutput.html#tut-f-strings" title="(in Python v3.14)"><spanclass="xref std std-ref">f-strings</span></a>, the <aclass="reference external" href="https://docs.python.org/3/library/functions.html#repr" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">repr()</span></code></a> of a an
object can be inserted instead of the <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#str" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">str</span></code></a> by using the <codeclass="docutils literal notranslate"><spanclass="pre">!r</span></code> modifier.
For example, we could just as well have written the method above as:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__repr__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">):</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">coefficients</span><spanclass="si">!r}</span><spanclass="s2">)"</span>
</pre></div>
</div>
</section>
<sectionid="object-equality">
<spanid="id1"></span><h3><spanclass="section-number">3.3.5. </span>Object equality<aclass="headerlink" href="#object-equality" title="Link to this heading">¶</a></h3>
<details>
<summary>
Video: object equality and test driven development</summary><divclass="video_wrapper" style="">
<iframeallowfullscreen="true" src="https://player.vimeo.com/video/488981397" 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=17aa405c-bf70-4007-b365-ae1c00da7976">watch this video on Panopto</a></p>
</details><p>When are two objects equal? For built-in types Python has equality rules which
broadly match the mathematical identities that you might expect. For example,
two numbers of different types are equal if their numerical value is equal:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="mi">2</span><spanclass="o">==</span><spanclass="mf">2.0</span>
<spanclass="gh">Out[1]: </span><spanclass="go">True</span>
<spanclass="gp">In [2]: </span><spanclass="mf">2.0</span><spanclass="o">==</span><spanclass="mi">2</span><spanclass="o">+</span><spanclass="mi">0</span><spanclass="n">j</span>
<spanclass="gh">Out[2]: </span><spanclass="go">True</span>
</pre></div>
</div>
<p>Similarly, intrinsic sequence types are equal when their contents are equal:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [3]: </span><spanclass="p">(</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="s2">"f"</span><spanclass="p">)</span><spanclass="o">==</span><spanclass="p">(</span><spanclass="mf">0.</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="o">+</span><spanclass="mi">0</span><spanclass="n">j</span><spanclass="p">,</span><spanclass="s1">'f'</span><spanclass="p">)</span>
<spanclass="gh">Out[3]: </span><spanclass="go">True</span>
<spanclass="gp">In [4]: </span><spanclass="p">(</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="s2">"f"</span><spanclass="p">)</span><spanclass="o">==</span><spanclass="p">(</span><spanclass="mf">0.</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="o">+</span><spanclass="mi">0</span><spanclass="n">j</span><spanclass="p">,</span><spanclass="s1">'g'</span><spanclass="p">)</span>
<spanclass="gh">Out[4]: </span><spanclass="go">False</span>
<spanclass="gp">In [5]: </span><spanclass="p">(</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="s2">"f"</span><spanclass="p">)</span><spanclass="o">==</span><spanclass="p">(</span><spanclass="mf">0.</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="o">+</span><spanclass="mi">0</span><spanclass="n">j</span><spanclass="p">)</span>
<spanclass="gh">Out[5]: </span><spanclass="go">False</span>
</pre></div>
</div>
<p>This mathematically pleasing state of affairs doesn’t, however, automatically
carry over to new classes. We might expect that two identically defined
polynomials might compare equal:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [6]: </span><spanclass="kn">from</span><spanclass="w"></span><spanclass="nn">example_code.polynomial</span><spanclass="w"></span><spanclass="kn">import</span><spanclass="n">Polynomial</span>
<spanclass="gp">In [7]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">))</span>
<spanclass="gp">In [8]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">))</span>
<spanclass="gp">In [9]: </span><spanclass="n">a</span><spanclass="o">==</span><spanclass="n">b</span>
<spanclass="gh">Out[9]: </span><spanclass="go">False</span>
</pre></div>
</div>
<p>The reason for this is obvious when one thinks about it: Python has no way to
know when two instances of a new class should be considered equal. Instead, it
falls back to comparing the unique identity of every object. This is accessible
using the built-in function <aclass="reference external" href="https://docs.python.org/3/library/functions.html#id" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">id()</span></code></a>:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [10]: </span><spanclass="nb">id</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="p">)</span>
<spanclass="gh">Out[10]: </span><spanclass="go">4487083344</span>
<spanclass="gp">In [11]: </span><spanclass="nb">id</span><spanclass="p">(</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="gh">Out[11]: </span><spanclass="go">4488256096</span>
</pre></div>
</div>
<p>This is a perfectly well-defined equality operator, but not a very
mathematically useful one. Fortunately, Python allows us to define a more useful
equality operator using the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__eq__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__eq__()</span></code></a><aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special method</span></a>. This
takes the current object and the object it is being compared to, and returns
<aclass="reference external" href="https://docs.python.org/3/library/constants.html#True" title="(in Python v3.14)"><codeclass="xref py py-data docutils literal notranslate"><spanclass="pre">True</span></code></a> or <aclass="reference external" href="https://docs.python.org/3/library/constants.html#False" title="(in Python v3.14)"><codeclass="xref py py-data docutils literal notranslate"><spanclass="pre">False</span></code></a> depending on whether the objects should be
considered equal. When we write <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span><spanclass="pre">==</span><spanclass="pre">b</span></code> in Python, what actually happens is
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a.__eq__(b)</span></code>.</p>
<p>A basic implementation of <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__eq__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__eq__()</span></code></a> that checks that the other
object is a <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> with the same
coefficients is:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__eq__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">other</span><spanclass="p">):</span>
<spanclass="k">return</span><spanclass="nb">isinstance</span><spanclass="p">(</span><spanclass="n">other</span><spanclass="p">,</span><spanclass="n">Polynomial</span><spanclass="p">)</span><spanclass="ow">and</span> \
<spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="o">==</span><spanclass="n">other</span><spanclass="o">.</span><spanclass="n">coefficients</span>
</pre></div>
</div>
<p>Equipped with this method, <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>
equality now behaves as we might expect.</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.polynomial</span><spanclass="w"></span><spanclass="kn">import</span><spanclass="n">Polynomial</span>
<spanclass="gp">In [2]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">))</span>
<spanclass="gp">In [3]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">))</span>
<spanclass="gp">In [4]: </span><spanclass="n">a</span><spanclass="o">==</span><spanclass="n">b</span>
<spanclass="gh">Out[4]: </span><spanclass="go">True</span>
</pre></div>
</div>
</section>
<sectionid="defining-arithmetic-options-on-objects">
<spanid="object-arithmetic"></span><h3><spanclass="section-number">3.3.6. </span>Defining arithmetic options on objects<aclass="headerlink" href="#defining-arithmetic-options-on-objects" title="Link to this heading">¶</a></h3>
<details>
<summary>
Video: polynomial addition.</summary><divclass="video_wrapper" style="">
<iframeallowfullscreen="true" src="https://player.vimeo.com/video/489009900" 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=d9a38b7e-d182-46f6-90fc-ae1c00da8682">watch this video on Panopto</a></p>
</details><p>It’s all very well to be able to compare our polynomial objects, but
we won’t really have captured the mathematical abstraction involved
unless we have at least some mathematical operations. We have already
observed that objects of some classes can be added. Is this true for
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>s?</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [2]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">0</span><spanclass="p">))</span>
<spanclass="gp">In [3]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,))</span>
<spanclass="gp">In [4]: </span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span>
<spanclass="gt">--------------------------------------------------------------------------</span>
<spanclass="ne">TypeError</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">a</span><spanclass="o">+</span><spanclass="n">b</span>
<spanclass="ne">TypeError</span>: unsupported operand type(s) for +: 'Polynomial' and 'Polynomial'
</pre></div>
</div>
<p>Once again, this is not so surprising since we haven’t
defined what addition of polynomials should mean. The <aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special
method</span></a> which defines addition is <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__add__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__add__()</span></code></a>. It takes the
object itself and another object and returns their sum. That is,
when you write <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span><spanclass="pre">+</span><spanclass="pre">b</span></code> in Python, then what actually happens is
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a.__add__(b)</span></code>.</p>
<p>Before we define our addition method, we first need to consider what
other objects it might make sense to add to a polynomial. Obviously, we
should be able to add two polynomials, but it also makes sense to add
a number to a polynomial. In either case, the result will be a new
polynomial, with coefficients equal to the sum of those of the
summands.</p>
<p>We also need to do something in the case where a user attempts to add to a
polynomial a value for which the operation makes no sense. For example, a user
might accidentally attempt to add a string to a polynomial. In this case, the
Python language specification requires that we return the special value
<aclass="reference external" href="https://docs.python.org/3/library/constants.html#NotImplemented" title="(in Python v3.14)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">NotImplemented</span></code></a>. Differentiating between the types of operands requires
two more Python features we have not yet met. One of these is the built in
function <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>, which tests whether an object is an instance of a
class. The other is the class <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">Number</span></code></a>, which we import from
the built-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> module. All Python 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">Number</span></code></a> so this provides a mechanism for checking whether the
other operand is a number. We will consider <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> and
<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">Number</span></code></a> in more detail when we look at <aclass="reference internal" href="7_inheritance.html#inheritance"><spanclass="std std-ref">inheritance</span></a> and <aclass="reference internal" href="10_further_object-oriented_features.html#abstract-base-classes"><spanclass="std std-ref">abstract base classes</span></a>.</p>
<p>Putting all this together, <aclass="reference internal" href="#polynomial-add"><spanclass="std std-numref">Listing 3.2</span></a> defines polynomial addition.</p>
<divclass="literal-block-wrapper docutils container" id="polynomial-add">
<divclass="code-block-caption"><spanclass="caption-number">Listing 3.2 </span><spanclass="caption-text">An implementation of addition for <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>.</span><aclass="headerlink" href="#polynomial-add" title="Link to this code">¶</a></div>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="linenos"> 1</span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__add__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">other</span><spanclass="p">):</span>
<spanclass="linenos"> 2</span><spanclass="k">if</span><spanclass="nb">isinstance</span><spanclass="p">(</span><spanclass="n">other</span><spanclass="p">,</span><spanclass="n">Number</span><spanclass="p">):</span>
<spanclass="linenos"> 3</span><spanclass="k">return</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="p">[</span><spanclass="mi">0</span><spanclass="p">]</span><spanclass="o">+</span><spanclass="n">other</span><spanclass="p">,)</span>
<spanclass="linenos"> 4</span><spanclass="o">+</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="p">[</span><spanclass="mi">1</span><spanclass="p">:])</span>
<spanclass="linenos"> 5</span>
<spanclass="linenos"> 6</span><spanclass="k">elif</span><spanclass="nb">isinstance</span><spanclass="p">(</span><spanclass="n">other</span><spanclass="p">,</span><spanclass="n">Polynomial</span><spanclass="p">):</span>
<spanclass="linenos"> 7</span><spanclass="c1"># Work out how many coefficient places the two polynomials have in</span>
<spanclass="linenos"> 8</span><spanclass="c1"># common.</span>
<spanclass="linenos"> 9</span><spanclass="n">common</span><spanclass="o">=</span><spanclass="nb">min</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">degree</span><spanclass="p">(),</span><spanclass="n">other</span><spanclass="o">.</span><spanclass="n">degree</span><spanclass="p">())</span><spanclass="o">+</span><spanclass="mi">1</span>
<spanclass="linenos">10</span><spanclass="c1"># Sum the common coefficient positions.</span>
<spanclass="linenos">11</span><spanclass="n">coefs</span><spanclass="o">=</span><spanclass="nb">tuple</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="k">for</span><spanclass="n">a</span><spanclass="p">,</span><spanclass="n">b</span><spanclass="ow">in</span><spanclass="nb">zip</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="p">[:</span><spanclass="n">common</span><spanclass="p">],</span>
<spanclass="linenos">12</span><spanclass="n">other</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="p">[:</span><spanclass="n">common</span><spanclass="p">]))</span>
<spanclass="linenos">13</span>
<spanclass="linenos">14</span><spanclass="c1"># Append the high degree coefficients from the higher degree</span>
<spanclass="linenos">15</span><spanclass="c1"># summand.</span>
<spanclass="linenos">16</span><spanclass="n">coefs</span><spanclass="o">+=</span><spanclass="bp">self</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="p">[</span><spanclass="n">common</span><spanclass="p">:]</span><spanclass="o">+</span><spanclass="n">other</span><spanclass="o">.</span><spanclass="n">coefficients</span><spanclass="p">[</span><spanclass="n">common</span><spanclass="p">:]</span>
<spanclass="linenos">17</span>
<spanclass="linenos">18</span><spanclass="k">return</span><spanclass="n">Polynomial</span><spanclass="p">(</span><spanclass="n">coefs</span><spanclass="p">)</span>
<spanclass="linenos">19</span>
<spanclass="linenos">20</span><spanclass="k">else</span><spanclass="p">:</span>
<spanclass="linenos">21</span><spanclass="k">return</span><spanclass="bp">NotImplemented</span>
</pre></div>
</div>
</div>
<p>Notice that we create a new <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> object for the result
each time: the sum of two polynomials is a different polynomial, it
doesn’t modify either polynomial in place.</p>
<p>Let’s try our new addition functionality in action:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [2]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">,</span><spanclass="mi">0</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">a</span><spanclass="p">)</span>
<spanclass="go">x^3 + 2x + 1</span>
<spanclass="gp">In [4]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">))</span>
<spanclass="gp">In [5]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="go">x</span>
<spanclass="gp">In [6]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="n">b</span><spanclass="p">)</span>
<spanclass="go">x^3 + 3x + 1</span>
<spanclass="gp">In [7]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="o">+</span><spanclass="mi">1</span><spanclass="p">)</span>
<spanclass="go">x^3 + 2x + 2</span>
<spanclass="gp">In [8]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="o">+</span><spanclass="n">a</span><spanclass="p">)</span>
<spanclass="gt">--------------------------------------------------------------------------</span>
<spanclass="ne">TypeError</span><spanclass="g g-Whitespace"></span>Traceback (most recent call last)
<spanclass="n">Cell</span><spanclass="n">In</span><spanclass="p">[</span><spanclass="mi">8</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="mi">1</span><spanclass="o">+</span><spanclass="n">a</span><spanclass="p">)</span>
<spanclass="ne">TypeError</span>: unsupported operand type(s) for +: 'int' and 'Polynomial'
</pre></div>
</div>
<p>So, everything proceeds as expected until we try to add a
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> to an integer. What happened? Remember that
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">1</span><spanclass="pre">+</span><spanclass="pre">a</span></code> causes Python to call <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">int.__add__(1,</span><spanclass="pre">a)</span></code>. What does that do?:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [9]: </span><spanclass="nb">int</span><spanclass="o">.</span><spanclass="fm">__add__</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="n">a</span><spanclass="p">)</span>
<spanclass="gh">Out[9]: </span><spanclass="go">NotImplemented</span>
</pre></div>
</div>
<p>Naturally, Python’s inbuilt <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> type knows nothing about our
new <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> class, so when we ask it to do the addition,
it returns <aclass="reference external" href="https://docs.python.org/3/library/constants.html#NotImplemented" title="(in Python v3.14)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">NotImplemented</span></code></a>. We could, however, tell
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> how to be added to an <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 Python
provides a mechanism for this. If the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__add__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__add__()</span></code></a> of the left hand
operand of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">+</span></code> returns <aclass="reference external" href="https://docs.python.org/3/library/constants.html#NotImplemented" title="(in Python v3.14)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">NotImplemented</span></code></a>, then Python tries the
reverse addition method, called <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__radd__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__radd__()</span></code></a>, on the right hand
operand. Because we know that polynomial addition is commutative,
we can define this very easily:</p>
<divclass="highlight-default notranslate"><divclass="highlight"><pre><span></span><spanclass="k">def</span><spanclass="w"></span><spanclass="fm">__radd__</span><spanclass="p">(</span><spanclass="bp">self</span><spanclass="p">,</span><spanclass="n">other</span><spanclass="p">):</span>
<spanclass="k">return</span><spanclass="bp">self</span><spanclass="o">+</span><spanclass="n">other</span>
</pre></div>
</div>
<p>With our newly enhanced <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> class, we can revisit the
previously problematic operation:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [2]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">1</span><spanclass="p">,</span><spanclass="mi">2</span><spanclass="p">,</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">))</span>
<spanclass="gp">In [3]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="o">+</span><spanclass="n">a</span><spanclass="p">)</span>
<spanclass="go">x^3 + 2x + 2</span>
</pre></div>
</div>
<p>Of course, addition is not the only arithmetic operator one might wish
to overload. A fully featured polynomial class will, at the very
minimum, need subtraction, multiplication (by a scalar or another
polynomial) and exponentiation by an integer power. The combination of
these, and particularly exponentiation, would allow the user to define
new polynomials in a particularly natural way, using Python’s
intrinsic operators:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">x</span><spanclass="o">=</span><spanclass="n">Polynomial</span><spanclass="p">((</span><spanclass="mi">0</span><spanclass="p">,</span><spanclass="mi">1</span><spanclass="p">))</span>
<spanclass="gp">In [2]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">x</span><spanclass="p">)</span>
<spanclass="go">x</span>
<spanclass="gp">In [3]: </span><spanclass="n">p</span><spanclass="o">=</span><spanclass="n">x</span><spanclass="o">**</span><spanclass="mi">3</span><spanclass="o">+</span><spanclass="mi">2</span><spanclass="o">*</span><spanclass="n">x</span><spanclass="o">+</span><spanclass="mi">2</span>
<spanclass="gp">In [4]: </span><spanclass="n">p</span>
<spanclass="go">Polynomial((2, 2, 0, 1))</span>
</pre></div>
</div>
<p>The <aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special method</span></a> names for further arithmetic operators are
given <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#numeric-types" title="(in Python v3.14)"><spanclass="xref std std-ref">in the Python documentation</span></a>. The
implementation of multiplication, exponentiation, and subtraction for
the <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> class is left as an exercise.</p>
</section>
<sectionid="creating-objects-that-act-like-functions">
<h3><spanclass="section-number">3.3.7. </span>Creating objects that act like functions<aclass="headerlink" href="#creating-objects-that-act-like-functions" title="Link to this heading">¶</a></h3>
<p>From a mathematical perspective, a real polynomial is a function. That is
to say, if:</p>
<divclass="math notranslate nohighlight">
\[f = x^2 + 2x + 1\]</div>
<p>then for any real <spanclass="math notranslate nohighlight">\(x\)</span>, <spanclass="math notranslate nohighlight">\(f(x)\)</span> is defined and is a real
number. We already know from the example of <aclass="reference external" href="https://docs.python.org/3/library/functions.html#abs" title="(in Python v3.14)"><codeclass="xref py py-func docutils literal notranslate"><spanclass="pre">abs()</span></code></a>, above, that
Python functions are objects. However, our challenge is the converse of
this: we have <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> objects which we would like to be
able to call like functions. The solution to our challenge is that
calling a function is an operation on an object similar to addition,
and Python provides another <aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special method</span></a> name for
this. <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">f(x)</span></code> is mapped to <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">f.__call__(x)</span></code>, so any Python object with a
<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> method behaves like a function, and any class
defining a <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> method in effect defines a new type of
function.</p>
</section>
</section>
<sectionid="encapsulation">
<h2><spanclass="section-number">3.4. </span>Encapsulation<aclass="headerlink" href="#encapsulation" title="Link to this heading">¶</a></h2>
<p>The property that objects have of bundling up data and methods in a
more-or-less opaque object with which other code can interact without
concerning itself with the internal details of the object is called
<aclass="reference internal" href="#term-encapsulation"><spanclass="xref std std-term">encapsulation</span></a>. Encapsulation is one of the core concepts in
object-oriented programming. In particular, encapsulation is key to
creating single objects representing high level mathematical
abstractions whose concrete realisation in code may require many
pieces of data and a large number of complex functions.</p>
</section>
<sectionid="assignment-and-instantiation">
<spanid="id2"></span><h2><spanclass="section-number">3.5. </span>Assignment and instantiation<aclass="headerlink" href="#assignment-and-instantiation" title="Link to this heading">¶</a></h2>
<p>One common confusion among new Python programmers concerns the distinction
between making new objects, and assigning new names to existing objects. The
key point to remember is that assignment in Python does not by itself create
new objects, only new variable names. For example:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [1]: </span><spanclass="n">a</span><spanclass="o">=</span><spanclass="nb">set</span><spanclass="p">()</span>
<spanclass="gp">In [2]: </span><spanclass="n">b</span><spanclass="o">=</span><spanclass="n">a</span>
</pre></div>
</div>
<p>The right hand side of the first line <aclass="reference internal" href="#term-instantiate"><spanclass="xref std std-term">instantiates</span></a> a new
set, and the assignment creates the name <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span></code> and associates it with the same
set. The second line is just an assignment: it associates the name <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">b</span></code> with the
same set. We can see the effect of this if we add an item to <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">b</span></code> and then look
at the contents of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span></code>:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [3]: </span><spanclass="n">b</span><spanclass="o">.</span><spanclass="n">add</span><spanclass="p">(</span><spanclass="mi">1</span><spanclass="p">)</span>
<spanclass="gp">In [4]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">a</span><spanclass="p">)</span>
<spanclass="go">{1}</span>
</pre></div>
</div>
<p>The same distinction between instantiating objects and making new references to
them is the cause of a frequent mistake when trying to create a list of empty
objects:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [5]: </span><spanclass="n">c</span><spanclass="o">=</span><spanclass="p">[</span><spanclass="nb">set</span><spanclass="p">()]</span><spanclass="o">*</span><spanclass="mi">5</span>
<spanclass="gp">In [6]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">c</span><spanclass="p">)</span>
<spanclass="go">[set(), set(), set(), set(), set()]</span>
</pre></div>
</div>
<p>The programmer almost certainly intended to create a list containing five empty
sets. Instead, they have created a list containing five references to the same
set:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [7]: </span><spanclass="n">c</span><spanclass="p">[</span><spanclass="mi">0</span><spanclass="p">]</span><spanclass="o">.</span><spanclass="n">add</span><spanclass="p">(</span><spanclass="mi">2</span><spanclass="p">)</span>
<spanclass="gp">In [8]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">c</span><spanclass="p">)</span>
<spanclass="go">[{2}, {2}, {2}, {2}, {2}]</span>
</pre></div>
</div>
<p>The right way to create a list of five empty sets is to use a list
comprehension. This will instantiate a different set for each entry in the
list:</p>
<divclass="highlight-ipython3 notranslate"><divclass="highlight"><pre><span></span><spanclass="gp">In [9]: </span><spanclass="n">d</span><spanclass="o">=</span><spanclass="p">[</span><spanclass="nb">set</span><spanclass="p">()</span><spanclass="k">for</span><spanclass="n">i</span><spanclass="ow">in</span><spanclass="nb">range</span><spanclass="p">(</span><spanclass="mi">5</span><spanclass="p">)]</span>
<spanclass="gp">In [10]: </span><spanclass="n">d</span><spanclass="p">[</span><spanclass="mi">0</span><spanclass="p">]</span><spanclass="o">.</span><spanclass="n">add</span><spanclass="p">(</span><spanclass="mi">2</span><spanclass="p">)</span>
<spanclass="gp">In [11]: </span><spanclass="nb">print</span><spanclass="p">(</span><spanclass="n">d</span><spanclass="p">)</span>
<spanclass="go">[{2}, set(), set(), set(), set()]</span>
</pre></div>
</div>
</section>
<sectionid="glossary">
<h2><spanclass="section-number">3.6. </span>Glossary<aclass="headerlink" href="#glossary" title="Link to this heading">¶</a></h2>
<blockquote>
<div><dlclass="simple glossary">
<dtid="term-abstraction">abstraction<aclass="headerlink" href="#term-abstraction" title="Link to this term">¶</a></dt><dd><p>A mathematical concept with a limited set of defined
properties. For the purposes of the abstraction, any other
properties that an object may have are disregarded.</p>
</dd>
<dtid="term-attribute">attribute<aclass="headerlink" href="#term-attribute" title="Link to this term">¶</a></dt><dd><p>A value encapsulated in another object, such as a
<aclass="reference internal" href="#term-class"><spanclass="xref std std-term">class</span></a>. Attributes are accessed using dot syntax, so if
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">b</span></code> is an attribute of <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span></code> then its value is accessed using the
syntax <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a.b</span></code>. <aclass="reference internal" href="#term-method"><spanclass="xref std std-term">Methods</span></a> are a special case of attributes.</p>
</dd>
<dtid="term-class">class<aclass="headerlink" href="#term-class" title="Link to this term">¶</a></dt><dtid="term-type">type<aclass="headerlink" href="#term-type" title="Link to this term">¶</a></dt><dd><p>An abstraction defined by a set of possible values, and a set
of operators valid for objects of that type. <aclass="reference external" href="https://docs.python.org/3/reference/compound_stmts.html#class" title="(in Python v3.14)"><codeclass="xref std std-keyword docutils literal notranslate"><spanclass="pre">Class</span></code></a> and <aclass="reference external" href="https://docs.python.org/3/library/functions.html#type" title="(in Python v3.14)"><codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">type</span></code></a> are essentially synonymous, though
the two words have different roles in Python code.</p>
</dd>
<dtid="term-concatenation">concatenation<aclass="headerlink" href="#term-concatenation" title="Link to this term">¶</a></dt><dd><p>The combination of two <aclass="reference external" href="https://docs.python.org/3/library/stdtypes.html#typesseq" title="(in Python v3.14)"><spanclass="xref std std-ref">sequences</span></a> by creating a new sequence containing
all of the items in the first sequence, followed by all of the items in
the second sequence. For example <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">(1,</span><spanclass="pre">2)</span><spanclass="pre">+</span><spanclass="pre">(3,</span><spanclass="pre">4)</span></code> is <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">(1,</span><spanclass="pre">2,</span><spanclass="pre">3,</span><spanclass="pre">4)</span></code>.</p>
</dd>
<dtid="term-constructor">constructor<aclass="headerlink" href="#term-constructor" title="Link to this term">¶</a></dt><dd><p>The <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__init__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__init__()</span></code></a> method of a <aclass="reference internal" href="#term-class"><spanclass="xref std std-term">class</span></a>. The constructor
is passed the new object as its first argument (<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code>) and is
responsible for setting up the object. The constructor modifies
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code> in place: constructors never return a value.</p>
</dd>
<dtid="term-data-attribute">data attribute<aclass="headerlink" href="#term-data-attribute" title="Link to this term">¶</a></dt><dd><p>An <aclass="reference internal" href="#term-attribute"><spanclass="xref std std-term">attribute</span></a> which is not a <aclass="reference internal" href="#term-method"><spanclass="xref std std-term">method</span></a>. As the name
suggests, these are used to store data in an object.</p>
</dd>
<dtid="term-encapsulation">encapsulation<aclass="headerlink" href="#term-encapsulation" title="Link to this term">¶</a></dt><dd><p>The bundling up of attributes and methods into an object which
can be dealt with as a single unit.</p>
</dd>
<dtid="term-infix-operator">infix operator<aclass="headerlink" href="#term-infix-operator" title="Link to this term">¶</a></dt><dd><p>A mathematical operator whose symbol is written between its <aclass="reference internal" href="#term-operands"><spanclass="xref std std-term">operands</span></a>.
Examples include addition, subtraction, division and multiplication.</p>
</dd>
<dtid="term-instance">instance<aclass="headerlink" href="#term-instance" title="Link to this term">¶</a></dt><dd><p>An object of a particular class. <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span></code> is an instance of
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">MyClass</span></code> means that <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span></code> has class <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">MyClass</span></code>. We will
return to this concept when we learn about <aclass="reference internal" href="7_inheritance.html#inheritance"><spanclass="std std-ref">inheritance</span></a>.</p>
</dd>
<dtid="term-instantiate">instantiate<aclass="headerlink" href="#term-instantiate" title="Link to this term">¶</a></dt><dd><p>To create an <aclass="reference internal" href="#term-instance"><spanclass="xref std std-term">instance</span></a> of a <aclass="reference internal" href="#term-class"><spanclass="xref std std-term">class</span></a> by
calling its <aclass="reference internal" href="#term-constructor"><spanclass="xref std std-term">constructor</span></a>.</p>
</dd>
<dtid="term-method">method<aclass="headerlink" href="#term-method" title="Link to this term">¶</a></dt><dtid="term-instance-method">instance method<aclass="headerlink" href="#term-instance-method" title="Link to this term">¶</a></dt><dd><p>A function defined within a <aclass="reference internal" href="#term-class"><spanclass="xref std std-term">class</span></a>. If <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a</span></code> is an
instance of <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">MyClass</span></code>, and <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">MyClass</span></code> has a <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">foo()</span></code> method then
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">a.foo()</span></code> is equivalent to <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">MyClass.foo(a)</span></code>. The first parameter
of an instance method is always named <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">self</span></code>.</p>
</dd>
<dtid="term-operands">operands<aclass="headerlink" href="#term-operands" title="Link to this term">¶</a></dt><dd><p>The input values to an operator. For example the operands to <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">+</span></code> are the
numbers being added (the summands), while the operands to exponentiation
are the base and exponent.</p>
</dd>
<dtid="term-pseudocode">pseudocode<aclass="headerlink" href="#term-pseudocode" title="Link to this term">¶</a></dt><dd><p>A description of an algorithm given in the form of a computer
program but without conforming to the rules of a particular
programming language, and employing mathematical notation or
plain text to express the algorithm in a human-readable form.</p>
</dd>
<dtid="term-special-method">special method<aclass="headerlink" href="#term-special-method" title="Link to this term">¶</a></dt><dtid="term-magic-method">magic method<aclass="headerlink" href="#term-magic-method" title="Link to this term">¶</a></dt><dd><p>A method which has special meaning in the Python
language. Special method names are used to define operations on
a <aclass="reference internal" href="#term-class"><spanclass="xref std std-term">class</span></a> such as arithmetic operators, indexing, or the
class <aclass="reference internal" href="#term-constructor"><spanclass="xref std std-term">constructor</span></a>. Special methods have names starting and ending
with a double underscore (<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">__</span></code>). See <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#specialnames" title="(in Python v3.14)"><spanclass="xref std std-ref">the Python documentation</span></a> for a technical description. Special methods
are sometimes informally called “magic methods”.</p>
</dd>
</dl>
</div></blockquote>
</section>
<sectionid="exercises">
<h2><spanclass="section-number">3.7. </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. The skeleton code contains a
<codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">polynomial</span></code> package with a version of the <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> class.</p>
<divclass="proof proof-type-exercise" id="id6">
<divclass="proof-title">
<spanclass="proof-type">Exercise 3.3</span>
</div><divclass="proof-content">
<p>Turn the exercises repository into an installable Pip package. As with
last chapter’s exercise, Pytest can’t test this so you’ll need to push to
GitHub and check that the autograding tests pass there.</p>
</div></div><divclass="proof proof-type-exercise" id="id7">
<divclass="proof-title">
<spanclass="proof-type">Exercise 3.4</span>
</div><divclass="proof-content">
<p>Implement the following operations on the <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> class.</p>
<olclass="arabic simple">
<li><p>Subtraction (<aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__sub__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__sub__()</span></code></a> and <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__rsub__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__rsub__()</span></code></a>).</p></li>
<li><p>Multiplication by another polynomial, and by a scalar
(<aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__mul__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__mul__()</span></code></a> and <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__rmul__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__rmul__()</span></code></a>).</p></li>
<li><p>Exponentiation by a positive integer power (<aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__pow__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__pow__()</span></code></a>). It
may be useful to know that all integers are 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>.</p></li>
<li><p>Polynomial evaluation at a scalar value (<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>).</p></li>
</ol>
<divclass="admonition hint">
<pclass="admonition-title">Hint</p>
<p>A limitation of the provided implementation of <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> is
that it doesn’t strip leading zeroes. This means that it doesn’t
correctly identify that, for example, <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Polynomial((1,</span><spanclass="pre">0))</span><spanclass="pre">==</span>
<spanclass="pre">Polynomial((1,))</span></code>. You may find it convenient to remove this limitation
by removing any leading zeroes in <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">Polynomial.__init__()</span></code>. If you do
this, take care that printing the zero polynomial still works.</p>
</div>
<divclass="admonition note">
<pclass="admonition-title">Note</p>
<p>Don’t forget to commit and push your changes, and make sure that the
tests pass on GitHub!</p>
</div>
</div></div><divclass="proof proof-type-exercise" id="id8">
<divclass="proof-title">
<spanclass="proof-type">Exercise 3.5</span>
</div><divclass="proof-content">
<p>Define a <codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">dx()</span></code> method on the <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> class which returns a
new <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> which is the derivative of that
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code>. Also define a function <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">derivative</span></code> in
<codeclass="file docutils literal notranslate"><spanclass="pre">polynomials.py</span></code> which takes a <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Polynomial</span></code> and returns its
derivative. Rather than duplicating code, you should implement the function
by calling the method.</p>
</div></div><divclass="proof proof-type-exercise" id="id9">
<divclass="proof-title">
<spanclass="proof-type">Exercise 3.6</span>
</div><divclass="proof-content">
<p>Inside the exercise repository, create a new <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">shape</span></code>
package containing a <codeclass="xref py py-mod docutils literal notranslate"><spanclass="pre">circle</span></code> module.</p>
<olclass="arabic">
<li><p>Create a <codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Circle</span></code> class
whose <aclass="reference internal" href="#term-constructor"><spanclass="xref std std-term">constructor</span></a> takes two user parameters, <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">centre</span></code> and <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">radius</span></code>.
<codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">centre</span></code> should be a length 2 sequence containing the two-dimensional
coordinates of the centre, while <codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">radius</span></code> is the radius of the circle.</p></li>
<li><p>Add an <aclass="reference internal" href="2_programs_in_files.html#modules"><spanclass="std std-ref">import</span></a> statement to <codeclass="file docutils literal notranslate"><spanclass="pre">shape/__init__.py</span></code> so
that the following code works:</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="kn">from</span><spanclass="w"></span><spanclass="nn">shape</span><spanclass="w"></span><spanclass="kn">import</span><spanclass="n">Circle</span>
</pre></div>
</div>
</li>
<li><p>Implement the <aclass="reference external" href="https://docs.python.org/3/reference/datamodel.html#object.__contains__" title="(in Python v3.14)"><codeclass="xref py py-meth docutils literal notranslate"><spanclass="pre">__contains__()</span></code></a><aclass="reference internal" href="#term-special-method"><spanclass="xref std std-term">special method</span></a> on the
<codeclass="xref py py-class docutils literal notranslate"><spanclass="pre">Circle</span></code> class so that it returns <aclass="reference external" href="https://docs.python.org/3/library/constants.html#True" title="(in Python v3.14)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">True</span></code></a> if a point (represented
by a length 2 sequence of coordinates) lies inside the circle. For
example, the following code should print <aclass="reference external" href="https://docs.python.org/3/library/constants.html#True" title="(in Python v3.14)"><codeclass="xref py py-obj docutils literal notranslate"><spanclass="pre">True</span></code></a>.</p>
<divclass="highlight-python3 notranslate"><divclass="highlight"><pre><span></span><spanclass="kn">from</span><spanclass="w"></span><spanclass="nn">shape</span><spanclass="w"></span><spanclass="kn">import</span><spanclass="n">Circle</span>
<spanclass="n">c</span><spanclass="o">=</span><spanclass="n">Circle</span><spanclass="p">((</span><spanclass="mf">1.</span><spanclass="p">,</span><spanclass="mf">0.</span><spanclass="p">),</span><spanclass="mi">2</span><spanclass="p">)</span>
<spanclass="nb">print</span><spanclass="p">((</span><spanclass="mf">0.5</span><spanclass="p">,</span><spanclass="mf">0.5</span><spanclass="p">)</span><spanclass="ow">in</span><spanclass="n">c</span><spanclass="p">)</span>
</pre></div>
</div>
</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="#id3">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>