-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path17-cls-meth.html
More file actions
637 lines (609 loc) · 43 KB
/
17-cls-meth.html
File metadata and controls
637 lines (609 loc) · 43 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
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Classes and methods — Pense Python 2e documentation</title>
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script type="text/javascript">
var DOCUMENTATION_OPTIONS = {
URL_ROOT: './',
VERSION: '2e',
COLLAPSE_INDEX: false,
FILE_SUFFIX: '.html',
HAS_SOURCE: true
};
</script>
<script type="text/javascript" src="_static/jquery.js"></script>
<script type="text/javascript" src="_static/underscore.js"></script>
<script type="text/javascript" src="_static/doctools.js"></script>
<link rel="top" title="Pense Python 2e documentation" href="index.html" />
<link rel="next" title="Inheritance" href="18-inherit.html" />
<link rel="prev" title="Classes and functions" href="16-cls-fn.html" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
</head>
<body role="document">
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<div class="section" id="classes-and-methods">
<h1>Classes and methods<a class="headerlink" href="#classes-and-methods" title="Permalink to this headline">¶</a></h1>
<p>Although we are using some of Python’s object-oriented features, the
programs from the last two chapters are not really object-oriented
because they don’t represent the relationships between
programmer-defined types and the functions that operate on them. The
next step is to transform those functions into methods that make the
relationships explicit.</p>
<p>Code examples from this chapter are available from
<a class="reference external" href="http://thinkpython2.com/code/Time2.py">http://thinkpython2.com/code/Time2.py</a>, and solutions to the exercises
are in <a class="reference external" href="http://thinkpython2.com/code/Point2_soln.py">http://thinkpython2.com/code/Point2_soln.py</a>.</p>
<div class="section" id="object-oriented-features">
<h2>Object-oriented features<a class="headerlink" href="#object-oriented-features" title="Permalink to this headline">¶</a></h2>
<p>Python is an <strong>object-oriented programming language</strong>, which means that
it provides features that support object-oriented programming, which has
these defining characteristics:</p>
<ul class="simple">
<li>Programs include class and method definitions.</li>
<li>Most of the computation is expressed in terms of operations on
objects.</li>
<li>Objects often represent things in the real world, and methods often
correspond to the ways things in the real world interact.</li>
</ul>
<p>For example, the Time class defined in Chapter [time] corresponds to the
way people record the time of day, and the functions we defined
correspond to the kinds of things people do with times. Similarly, the
Point and Rectangle classes in Chapter [clobjects] correspond to the
mathematical concepts of a point and a rectangle.</p>
<p>So far, we have not taken advantage of the features Python provides to
support object-oriented programming. These features are not strictly
necessary; most of them provide alternative syntax for things we have
already done. But in many cases, the alternative is more concise and
more accurately conveys the structure of the program.</p>
<p>For example, in Time1.py there is no obvious connection between the
class definition and the function definitions that follow. With some
examination, it is apparent that every function takes at least one Time
object as an argument.</p>
<p>This observation is the motivation for <strong>methods</strong>; a method is a
function that is associated with a particular class. We have seen
methods for strings, lists, dictionaries and tuples. In this chapter, we
will define methods for programmer-defined types.</p>
<p>Methods are semantically the same as functions, but there are two
syntactic differences:</p>
<ul class="simple">
<li>Methods are defined inside a class definition in order to make the
relationship between the class and the method explicit.</li>
<li>The syntax for invoking a method is different from the syntax for
calling a function.</li>
</ul>
<p>In the next few sections, we will take the functions from the previous
two chapters and transform them into methods. This transformation is
purely mechanical; you can do it by following a sequence of steps. If
you are comfortable converting from one form to another, you will be
able to choose the best form for whatever you are doing.</p>
</div>
<div class="section" id="printing-objects">
<h2>Printing objects<a class="headerlink" href="#printing-objects" title="Permalink to this headline">¶</a></h2>
<p>In Chapter [time], we defined a class named Time and in
Section [isafter], you wrote a function named <code class="docutils literal"><span class="pre">print_time</span></code>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Time</span><span class="p">:</span>
<span class="sd">"""Represents the time of day."""</span>
<span class="k">def</span> <span class="nf">print_time</span><span class="p">(</span><span class="n">time</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="si">%.2d</span><span class="s">:</span><span class="si">%.2d</span><span class="s">:</span><span class="si">%.2d</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">hour</span><span class="p">,</span> <span class="n">time</span><span class="o">.</span><span class="n">minute</span><span class="p">,</span> <span class="n">time</span><span class="o">.</span><span class="n">second</span><span class="p">))</span>
</pre></div>
</div>
<p>To call this function, you have to pass a Time object as an argument:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">start</span> <span class="o">=</span> <span class="n">Time</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">start</span><span class="o">.</span><span class="n">hour</span> <span class="o">=</span> <span class="mi">9</span>
<span class="gp">>>> </span><span class="n">start</span><span class="o">.</span><span class="n">minute</span> <span class="o">=</span> <span class="mi">45</span>
<span class="gp">>>> </span><span class="n">start</span><span class="o">.</span><span class="n">second</span> <span class="o">=</span> <span class="mo">00</span>
<span class="gp">>>> </span><span class="n">print_time</span><span class="p">(</span><span class="n">start</span><span class="p">)</span>
<span class="go">09:45:00</span>
</pre></div>
</div>
<p>To make <code class="docutils literal"><span class="pre">print_time</span></code> a method, all we have to do is move the function
definition inside the class definition. Notice the change in
indentation.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Time</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">print_time</span><span class="p">(</span><span class="n">time</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="si">%.2d</span><span class="s">:</span><span class="si">%.2d</span><span class="s">:</span><span class="si">%.2d</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">hour</span><span class="p">,</span> <span class="n">time</span><span class="o">.</span><span class="n">minute</span><span class="p">,</span> <span class="n">time</span><span class="o">.</span><span class="n">second</span><span class="p">))</span>
</pre></div>
</div>
<p>Now there are two ways to call <code class="docutils literal"><span class="pre">print_time</span></code>. The first (and less
common) way is to use function syntax:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">Time</span><span class="o">.</span><span class="n">print_time</span><span class="p">(</span><span class="n">start</span><span class="p">)</span>
<span class="go">09:45:00</span>
</pre></div>
</div>
<p>In this use of dot notation, Time is the name of the class, and
<code class="docutils literal"><span class="pre">print_time</span></code> is the name of the method. start is passed as a
parameter.</p>
<p>The second (and more concise) way is to use method syntax:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">start</span><span class="o">.</span><span class="n">print_time</span><span class="p">()</span>
<span class="go">09:45:00</span>
</pre></div>
</div>
<p>In this use of dot notation, <code class="docutils literal"><span class="pre">print_time</span></code> is the name of the method
(again), and start is the object the method is invoked on, which is
called the <strong>subject</strong>. Just as the subject of a sentence is what the
sentence is about, the subject of a method invocation is what the method
is about.</p>
<p>Inside the method, the subject is assigned to the first parameter, so in
this case start is assigned to time.</p>
<p>By convention, the first parameter of a method is called self, so it
would be more common to write <code class="docutils literal"><span class="pre">print_time</span></code> like this:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Time</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">print_time</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="s">'</span><span class="si">%.2d</span><span class="s">:</span><span class="si">%.2d</span><span class="s">:</span><span class="si">%.2d</span><span class="s">'</span> <span class="o">%</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">hour</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">minute</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">second</span><span class="p">))</span>
</pre></div>
</div>
<p>The reason for this convention is an implicit metaphor:</p>
<ul class="simple">
<li>The syntax for a function call, <code class="docutils literal"><span class="pre">print_time(start)</span></code>, suggests that
the function is the active agent. It says something like, “Hey
<code class="docutils literal"><span class="pre">print_time</span></code>! Here’s an object for you to print.”</li>
<li>In object-oriented programming, the objects are the active agents. A
method invocation like <code class="docutils literal"><span class="pre">start.print_time()</span></code> says “Hey start! Please
print yourself.”</li>
</ul>
<p>This change in perspective might be more polite, but it is not obvious
that it is useful. In the examples we have seen so far, it may not be.
But sometimes shifting responsibility from the functions onto the
objects makes it possible to write more versatile functions (or
methods), and makes it easier to maintain and reuse code.</p>
<p>As an exercise, rewrite <code class="docutils literal"><span class="pre">time_to_int</span></code> (from Section [prototype]) as a
method. You might be tempted to rewrite <code class="docutils literal"><span class="pre">int_to_time</span></code> as a method,
too, but that doesn’t really make sense because there would be no object
to invoke it on.</p>
</div>
<div class="section" id="another-example">
<h2>Another example<a class="headerlink" href="#another-example" title="Permalink to this headline">¶</a></h2>
<p>Here’s a version of increment (from Section [increment]) rewritten as a
method:</p>
<div class="highlight-python"><div class="highlight"><pre># inside class Time:
def increment(self, seconds):
seconds += self.time_to_int()
return int_to_time(seconds)
</pre></div>
</div>
<p>This version assumes that <code class="docutils literal"><span class="pre">time_to_int</span></code> is written as a method. Also,
note that it is a pure function, not a modifier.</p>
<p>Here’s how you would invoke increment:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">start</span><span class="o">.</span><span class="n">print_time</span><span class="p">()</span>
<span class="go">09:45:00</span>
<span class="gp">>>> </span><span class="n">end</span> <span class="o">=</span> <span class="n">start</span><span class="o">.</span><span class="n">increment</span><span class="p">(</span><span class="mi">1337</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">end</span><span class="o">.</span><span class="n">print_time</span><span class="p">()</span>
<span class="go">10:07:17</span>
</pre></div>
</div>
<p>The subject, start, gets assigned to the first parameter, self. The
argument, 1337, gets assigned to the second parameter, seconds.</p>
<p>This mechanism can be confusing, especially if you make an error. For
example, if you invoke increment with two arguments, you get:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">end</span> <span class="o">=</span> <span class="n">start</span><span class="o">.</span><span class="n">increment</span><span class="p">(</span><span class="mi">1337</span><span class="p">,</span> <span class="mi">460</span><span class="p">)</span>
<span class="go">TypeError: increment() takes 2 positional arguments but 3 were given</span>
</pre></div>
</div>
<p>The error message is initially confusing, because there are only two
arguments in parentheses. But the subject is also considered an
argument, so all together that’s three.</p>
<p>By the way, a <strong>positional argument</strong> is an argument that doesn’t have a
parameter name; that is, it is not a keyword argument. In this function
call:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">sketch</span><span class="p">(</span><span class="n">parrot</span><span class="p">,</span> <span class="n">cage</span><span class="p">,</span> <span class="n">dead</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</pre></div>
</div>
<p>parrot and cage are positional, and dead is a keyword argument.</p>
</div>
<div class="section" id="a-more-complicated-example">
<h2>A more complicated example<a class="headerlink" href="#a-more-complicated-example" title="Permalink to this headline">¶</a></h2>
<p>Rewriting <code class="docutils literal"><span class="pre">is_after</span></code> (from Section [isafter]) is slightly more
complicated because it takes two Time objects as parameters. In this
case it is conventional to name the first parameter self and the second
parameter other:</p>
<div class="highlight-python"><div class="highlight"><pre># inside class Time:
def is_after(self, other):
return self.time_to_int() > other.time_to_int()
</pre></div>
</div>
<p>To use this method, you have to invoke it on one object and pass the
other as an argument:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">end</span><span class="o">.</span><span class="n">is_after</span><span class="p">(</span><span class="n">start</span><span class="p">)</span>
<span class="go">True</span>
</pre></div>
</div>
<p>One nice thing about this syntax is that it almost reads like English:
“end is after start?”</p>
</div>
<div class="section" id="the-init-method">
<h2>The init method<a class="headerlink" href="#the-init-method" title="Permalink to this headline">¶</a></h2>
<p>The init method (short for “initialization”) is a special method that
gets invoked when an object is instantiated. Its full name is
<code class="docutils literal"><span class="pre">__init__</span></code> (two underscore characters, followed by init, and then two
more underscores). An init method for the Time class might look like
this:</p>
<div class="highlight-python"><div class="highlight"><pre># inside class Time:
def __init__(self, hour=0, minute=0, second=0):
self.hour = hour
self.minute = minute
self.second = second
</pre></div>
</div>
<p>It is common for the parameters of <code class="docutils literal"><span class="pre">__init__</span></code> to have the same names
as the attributes. The statement</p>
<div class="highlight-python"><div class="highlight"><pre><span class="bp">self</span><span class="o">.</span><span class="n">hour</span> <span class="o">=</span> <span class="n">hour</span>
</pre></div>
</div>
<p>stores the value of the parameter hour as an attribute of self.</p>
<p>The parameters are optional, so if you call Time with no arguments, you
get the default values.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">time</span> <span class="o">=</span> <span class="n">Time</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">time</span><span class="o">.</span><span class="n">print_time</span><span class="p">()</span>
<span class="go">00:00:00</span>
</pre></div>
</div>
<p>If you provide one argument, it overrides hour:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">time</span> <span class="o">=</span> <span class="n">Time</span> <span class="p">(</span><span class="mi">9</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">time</span><span class="o">.</span><span class="n">print_time</span><span class="p">()</span>
<span class="go">09:00:00</span>
</pre></div>
</div>
<p>If you provide two arguments, they override hour and minute.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">time</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="mi">45</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">time</span><span class="o">.</span><span class="n">print_time</span><span class="p">()</span>
<span class="go">09:45:00</span>
</pre></div>
</div>
<p>And if you provide three arguments, they override all three default
values.</p>
<p>As an exercise, write an init method for the Point class that takes x
and y as optional parameters and assigns them to the corresponding
attributes.</p>
</div>
<div class="section" id="the-str-method">
<h2>The __str__ method<a class="headerlink" href="#the-str-method" title="Permalink to this headline">¶</a></h2>
<p><code class="docutils literal"><span class="pre">__str__</span></code> is a special method, like <code class="docutils literal"><span class="pre">__init__</span></code>, that is supposed to
return a string representation of an object.</p>
<p>For example, here is a str method for Time objects:</p>
<div class="highlight-python"><div class="highlight"><pre># inside class Time:
def __str__(self):
return '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second)
</pre></div>
</div>
<p>When you print an object, Python invokes the str method:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">time</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="mi">45</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">print</span><span class="p">(</span><span class="n">time</span><span class="p">)</span>
<span class="go">09:45:00</span>
</pre></div>
</div>
<p>When I write a new class, I almost always start by writing <code class="docutils literal"><span class="pre">__init__</span></code>,
which makes it easier to instantiate objects, and <code class="docutils literal"><span class="pre">__str__</span></code>, which is
useful for debugging.</p>
<p>As an exercise, write a str method for the Point class. Create a Point
object and print it.</p>
</div>
<div class="section" id="operator-overloading">
<h2>Operator overloading<a class="headerlink" href="#operator-overloading" title="Permalink to this headline">¶</a></h2>
<p>By defining other special methods, you can specify the behavior of
operators on programmer-defined types. For example, if you define a
method named <code class="docutils literal"><span class="pre">__add__</span></code> for the Time class, you can use the + operator
on Time objects.</p>
<p>Here is what the definition might look like:</p>
<div class="highlight-python"><div class="highlight"><pre># inside class Time:
def __add__(self, other):
seconds = self.time_to_int() + other.time_to_int()
return int_to_time(seconds)
</pre></div>
</div>
<p>And here is how you could use it:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">start</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="mi">45</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">duration</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">35</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">print</span><span class="p">(</span><span class="n">start</span> <span class="o">+</span> <span class="n">duration</span><span class="p">)</span>
<span class="go">11:20:00</span>
</pre></div>
</div>
<p>When you apply the + operator to Time objects, Python invokes
<code class="docutils literal"><span class="pre">__add__</span></code>. When you print the result, Python invokes <code class="docutils literal"><span class="pre">__str__</span></code>. So
there is a lot happening behind the scenes!</p>
<p>Changing the behavior of an operator so that it works with
programmer-defined types is called <strong>operator overloading</strong>. For every
operator in Python there is a corresponding special method, like
<code class="docutils literal"><span class="pre">__add__</span></code>. For more details, see
<a class="reference external" href="http://docs.python.org/3/reference/datamodel.html#specialnames">http://docs.python.org/3/reference/datamodel.html#specialnames</a>.</p>
<p>As an exercise, write an add method for the Point class.</p>
</div>
<div class="section" id="type-based-dispatch">
<h2>Type-based dispatch<a class="headerlink" href="#type-based-dispatch" title="Permalink to this headline">¶</a></h2>
<p>In the previous section we added two Time objects, but you also might
want to add an integer to a Time object. The following is a version of
<code class="docutils literal"><span class="pre">__add__</span></code> that checks the type of other and invokes either
<code class="docutils literal"><span class="pre">add_time</span></code> or increment:</p>
<div class="highlight-python"><div class="highlight"><pre># inside class Time:
def __add__(self, other):
if isinstance(other, Time):
return self.add_time(other)
else:
return self.increment(other)
def add_time(self, other):
seconds = self.time_to_int() + other.time_to_int()
return int_to_time(seconds)
def increment(self, seconds):
seconds += self.time_to_int()
return int_to_time(seconds)
</pre></div>
</div>
<p>The built-in function isinstance takes a value and a class object, and
returns True if the value is an instance of the class.</p>
<p>If other is a Time object, <code class="docutils literal"><span class="pre">__add__</span></code> invokes <code class="docutils literal"><span class="pre">add_time</span></code>. Otherwise
it assumes that the parameter is a number and invokes increment. This
operation is called a <strong>type-based dispatch</strong> because it dispatches the
computation to different methods based on the type of the arguments.</p>
<p>Here are examples that use the + operator with different types:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">start</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="mi">45</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">duration</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">35</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">print</span><span class="p">(</span><span class="n">start</span> <span class="o">+</span> <span class="n">duration</span><span class="p">)</span>
<span class="go">11:20:00</span>
<span class="gp">>>> </span><span class="k">print</span><span class="p">(</span><span class="n">start</span> <span class="o">+</span> <span class="mi">1337</span><span class="p">)</span>
<span class="go">10:07:17</span>
</pre></div>
</div>
<p>Unfortunately, this implementation of addition is not commutative. If
the integer is the first operand, you get</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="k">print</span><span class="p">(</span><span class="mi">1337</span> <span class="o">+</span> <span class="n">start</span><span class="p">)</span>
<span class="go">TypeError: unsupported operand type(s) for +: 'int' and 'instance'</span>
</pre></div>
</div>
<p>The problem is, instead of asking the Time object to add an integer,
Python is asking an integer to add a Time object, and it doesn’t know
how. But there is a clever solution for this problem: the special method
<code class="docutils literal"><span class="pre">__radd__</span></code>, which stands for “right-side add”. This method is invoked
when a Time object appears on the right side of the + operator. Here’s
the definition:</p>
<div class="highlight-python"><div class="highlight"><pre># inside class Time:
def __radd__(self, other):
return self.__add__(other)
</pre></div>
</div>
<p>And here’s how it’s used:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="k">print</span><span class="p">(</span><span class="mi">1337</span> <span class="o">+</span> <span class="n">start</span><span class="p">)</span>
<span class="go">10:07:17</span>
</pre></div>
</div>
<p>As an exercise, write an add method for Points that works with either a
Point object or a tuple:</p>
<ul class="simple">
<li>If the second operand is a Point, the method should return a new
Point whose <span class="math">x</span> coordinate is the sum of the <span class="math">x</span>
coordinates of the operands, and likewise for the <span class="math">y</span>
coordinates.</li>
<li>If the second operand is a tuple, the method should add the first
element of the tuple to the <span class="math">x</span> coordinate and the second
element to the <span class="math">y</span> coordinate, and return a new Point with the
result.</li>
</ul>
</div>
<div class="section" id="polymorphism">
<h2>Polymorphism<a class="headerlink" href="#polymorphism" title="Permalink to this headline">¶</a></h2>
<p>Type-based dispatch is useful when it is necessary, but (fortunately) it
is not always necessary. Often you can avoid it by writing functions
that work correctly for arguments with different types.</p>
<p>Many of the functions we wrote for strings also work for other sequence
types. For example, in Section [histogram] we used histogram to count
the number of times each letter appears in a word.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">histogram</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<span class="n">d</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">s</span><span class="p">:</span>
<span class="k">if</span> <span class="n">c</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">d</span><span class="p">:</span>
<span class="n">d</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">d</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">=</span> <span class="n">d</span><span class="p">[</span><span class="n">c</span><span class="p">]</span><span class="o">+</span><span class="mi">1</span>
<span class="k">return</span> <span class="n">d</span>
</pre></div>
</div>
<p>This function also works for lists, tuples, and even dictionaries, as
long as the elements of s are hashable, so they can be used as keys in
d.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">t</span> <span class="o">=</span> <span class="p">[</span><span class="s">'spam'</span><span class="p">,</span> <span class="s">'egg'</span><span class="p">,</span> <span class="s">'spam'</span><span class="p">,</span> <span class="s">'spam'</span><span class="p">,</span> <span class="s">'bacon'</span><span class="p">,</span> <span class="s">'spam'</span><span class="p">]</span>
<span class="gp">>>> </span><span class="n">histogram</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
<span class="go">{'bacon': 1, 'egg': 1, 'spam': 4}</span>
</pre></div>
</div>
<p>Functions that work with several types are called <strong>polymorphic</strong>.
Polymorphism can facilitate code reuse. For example, the built-in
function sum, which adds the elements of a sequence, works as long as
the elements of the sequence support addition.</p>
<p>Since Time objects provide an add method, they work with sum:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">t1</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">43</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">t2</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">41</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">t3</span> <span class="o">=</span> <span class="n">Time</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="mi">37</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">total</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">([</span><span class="n">t1</span><span class="p">,</span> <span class="n">t2</span><span class="p">,</span> <span class="n">t3</span><span class="p">])</span>
<span class="gp">>>> </span><span class="k">print</span><span class="p">(</span><span class="n">total</span><span class="p">)</span>
<span class="go">23:01:00</span>
</pre></div>
</div>
<p>In general, if all of the operations inside a function work with a given
type, the function works with that type.</p>
<p>The best kind of polymorphism is the unintentional kind, where you
discover that a function you already wrote can be applied to a type you
never planned for.</p>
</div>
<div class="section" id="debugging">
<h2>Debugging<a class="headerlink" href="#debugging" title="Permalink to this headline">¶</a></h2>
<p>It is legal to add attributes to objects at any point in the execution
of a program, but if you have objects with the same type that don’t have
the same attributes, it is easy to make mistakes. It is considered a
good idea to initialize all of an object’s attributes in the init
method.</p>
<p>If you are not sure whether an object has a particular attribute, you
can use the built-in function hasattr (see Section [hasattr]).</p>
<p>Another way to access attributes is the built-in function vars, which
takes an object and returns a dictionary that maps from attribute names
(as strings) to their values:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">p</span> <span class="o">=</span> <span class="n">Point</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span>
<span class="gp">>>> </span><span class="nb">vars</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="go">{'y': 4, 'x': 3}</span>
</pre></div>
</div>
<p>For purposes of debugging, you might find it useful to keep this
function handy:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">print_attributes</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="k">for</span> <span class="n">attr</span> <span class="ow">in</span> <span class="nb">vars</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="k">print</span><span class="p">(</span><span class="n">attr</span><span class="p">,</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">attr</span><span class="p">))</span>
</pre></div>
</div>
<p><code class="docutils literal"><span class="pre">print_attributes</span></code> traverses the dictionary and prints each attribute
name and its corresponding value.</p>
<p>The built-in function getattr takes an object and an attribute name (as
a string) and returns the attribute’s value.</p>
</div>
<div class="section" id="interface-and-implementation">
<h2>Interface and implementation<a class="headerlink" href="#interface-and-implementation" title="Permalink to this headline">¶</a></h2>
<p>One of the goals of object-oriented design is to make software more
maintainable, which means that you can keep the program working when
other parts of the system change, and modify the program to meet new
requirements.</p>
<p>A design principle that helps achieve that goal is to keep interfaces
separate from implementations. For objects, that means that the methods
a class provides should not depend on how the attributes are
represented.</p>
<p>For example, in this chapter we developed a class that represents a time
of day. Methods provided by this class include <code class="docutils literal"><span class="pre">time_to_int</span></code>,
<code class="docutils literal"><span class="pre">is_after</span></code>, and <code class="docutils literal"><span class="pre">add_time</span></code>.</p>
<p>We could implement those methods in several ways. The details of the
implementation depend on how we represent time. In this chapter, the
attributes of a Time object are hour, minute, and second.</p>
<p>As an alternative, we could replace these attributes with a single
integer representing the number of seconds since midnight. This
implementation would make some methods, like <code class="docutils literal"><span class="pre">is_after</span></code>, easier to
write, but it makes other methods harder.</p>
<p>After you deploy a new class, you might discover a better
implementation. If other parts of the program are using your class, it
might be time-consuming and error-prone to change the interface.</p>
<p>But if you designed the interface carefully, you can change the
implementation without changing the interface, which means that other
parts of the program don’t have to change.</p>
</div>
<div class="section" id="glossary">
<span id="glossary17"></span><h2>Glossary<a class="headerlink" href="#glossary" title="Permalink to this headline">¶</a></h2>
<dl class="docutils">
<dt>linguagem orientada a objetos (<em>object-oriented language</em>)</dt>
<dd>A language that provides features, such as programmer-defined types and methods, that facilitate object-oriented programming.</dd>
<dt>programação orientada a objetos (<em>object-oriented programming</em>)</dt>
<dd>A style of programming in which data and the operations that manipulate it are organized into classes and methods.</dd>
<dt>método (<em>method</em>)</dt>
<dd>A function that is defined inside a class definition and is invoked on instances of that class.</dd>
<dt>sujeito (<em>subject</em>)</dt>
<dd>The object a method is invoked on.</dd>
<dt>argumento posicional (<em>positional argument</em>)</dt>
<dd>An argument that does not include a parameter name, so it is not a keyword argument.</dd>
<dt>sobrecarga de operadores (<em>operator overloading</em>)</dt>
<dd>Changing the behavior of an operator like + so it works with a programmer-defined type.</dd>
<dt>despacho por tipo (<em>type-based dispatch</em>)</dt>
<dd>A programming pattern that checks the type of an operand and invokes different functions for different types.</dd>
<dt>polimórfico (<em>polymorphic</em>)</dt>
<dd>Pertaining to a function that can work with more than one type.</dd>
<dt>ocultação de informações (<em>information hiding</em>)</dt>
<dd>The principle that the interface provided by an object should not depend on its implementation, in particular the representation of its attributes.</dd>
</dl>
</div>
<div class="section" id="exercises">
<h2>Exercises<a class="headerlink" href="#exercises" title="Permalink to this headline">¶</a></h2>
<p>Download the code from this chapter from
<a class="reference external" href="http://thinkpython2.com/code/Time2.py">http://thinkpython2.com/code/Time2.py</a>. Change the attributes of Time to
be a single integer representing seconds since midnight. Then modify the
methods (and the function <code class="docutils literal"><span class="pre">int_to_time</span></code>) to work with the new
implementation. You should not have to modify the test code in main.
When you are done, the output should be the same as before. Solution:
<a class="reference external" href="http://thinkpython2.com/code/Time2_soln.py">http://thinkpython2.com/code/Time2_soln.py</a>.</p>
<p>[kangaroo]</p>
<p>This exercise is a cautionary tale about one of the most common, and
difficult to find, errors in Python. Write a definition for a class
named Kangaroo with the following methods:</p>
<ol class="arabic simple">
<li>An <code class="docutils literal"><span class="pre">__init__</span></code> method that initializes an attribute named
<code class="docutils literal"><span class="pre">pouch_contents</span></code> to an empty list.</li>
<li>A method named <code class="docutils literal"><span class="pre">put_in_pouch</span></code> that takes an object of any type and
adds it to <code class="docutils literal"><span class="pre">pouch_contents</span></code>.</li>
<li>A <code class="docutils literal"><span class="pre">__str__</span></code> method that returns a string representation of the
Kangaroo object and the contents of the pouch.</li>
</ol>
<p>Test your code by creating two Kangaroo objects, assigning them to
variables named kanga and roo, and then adding roo to the contents of
kanga’s pouch.</p>
<p>Download <a class="reference external" href="http://thinkpython2.com/code/BadKangaroo.py">http://thinkpython2.com/code/BadKangaroo.py</a>. It contains a
solution to the previous problem with one big, nasty bug. Find and fix
the bug.</p>
<p>If you get stuck, you can download
<a class="reference external" href="http://thinkpython2.com/code/GoodKangaroo.py">http://thinkpython2.com/code/GoodKangaroo.py</a>, which explains the problem
and demonstrates a solution.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<h3><a href="index.html">Table Of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Classes and methods</a><ul>
<li><a class="reference internal" href="#object-oriented-features">Object-oriented features</a></li>
<li><a class="reference internal" href="#printing-objects">Printing objects</a></li>
<li><a class="reference internal" href="#another-example">Another example</a></li>
<li><a class="reference internal" href="#a-more-complicated-example">A more complicated example</a></li>
<li><a class="reference internal" href="#the-init-method">The init method</a></li>
<li><a class="reference internal" href="#the-str-method">The __str__ method</a></li>
<li><a class="reference internal" href="#operator-overloading">Operator overloading</a></li>
<li><a class="reference internal" href="#type-based-dispatch">Type-based dispatch</a></li>
<li><a class="reference internal" href="#polymorphism">Polymorphism</a></li>
<li><a class="reference internal" href="#debugging">Debugging</a></li>
<li><a class="reference internal" href="#interface-and-implementation">Interface and implementation</a></li>
<li><a class="reference internal" href="#glossary">Glossary</a></li>
<li><a class="reference internal" href="#exercises">Exercises</a></li>
</ul>
</li>
</ul>
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="index.html">Documentation overview</a><ul>
<li>Previous: <a href="16-cls-fn.html" title="previous chapter">Classes and functions</a></li>
<li>Next: <a href="18-inherit.html" title="next chapter">Inheritance</a></li>
</ul></li>
</ul>
</div>
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="_sources/17-cls-meth.txt"
rel="nofollow">Show Source</a></li>
</ul>
</div>
<div id="searchbox" style="display: none" role="search">
<h3>Quick search</h3>
<form class="search" action="search.html" method="get">
<input type="text" name="q" />
<input type="submit" value="Go" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
<p class="searchtip" style="font-size: 90%">
Enter search terms or a module, class or function name.
</p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer">
©2015, Allen B. Downey.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 1.3.1</a>
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.6</a>
|
<a href="_sources/17-cls-meth.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>