forked from bailiangrui/git-basics-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paththinkpython2018.html
574 lines (536 loc) · 38.8 KB
/
thinkpython2018.html
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
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<meta name="generator" content="hevea 2.09">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="thinkpython2.css">
<title>Classes and methods</title>
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#"><strong>Think Python</strong> - How to Think like a Computer Scientist (2e) <em>by Allen B. Downey</em></a>
</div>
<div>
<ul class="nav navbar-nav navbar-right">
<li><a href="http://greenteapress.com/thinkpython2/html/index.html"><span class="glyphicon glyphicon glyphicon-book" aria-hidden="true"></span></a></li>
<li><a href="thinkpython2017.html"><span class="glyphicon glyphicon glyphicon-menu-left" aria-hidden="true"></span></a></li>
<li><a href="index.html"><span class="glyphicon glyphicon glyphicon-home" aria-hidden="true"></span></a></li>
<li><a href="thinkpython2019.html"><span class="glyphicon glyphicon glyphicon-menu-right" aria-hidden="true"></span></a></li>
<li><a href="http://amzn.to/1VUYQUU"><span class="glyphicon glyphicon glyphicon-shopping-cart" aria-hidden="true"></span></a></li>
</ul>
<div>
</div><!-- /.container-fluid -->
</nav>
<table>
<tr>
<td valign="top" width="100" bgcolor="#b6459a" id="col-left">
</td>
<td valign="top" id="content">
<p>
<h1 class="chapter" id="sec195">Chapter 17  Classes and methods</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 href="http://thinkpython2.com/code/Time2.py"><span class="c004">http://thinkpython2.com/code/Time2.py</span></a>, and solutions
to the exercises are in <a href="http://thinkpython2.com/code/Point2_soln.py"><span class="c004">http://thinkpython2.com/code/Point2_soln.py</span></a>.</p>
<h2 class="section" id="sec196">17.1  Object-oriented features</h2>
<p>
<a id="hevea_default1457"></a></p><p>Python is an <span class="c010">object-oriented programming language</span>, which means
that it provides features that support object-oriented
programming, which has these defining characteristics:</p><ul class="itemize"><li class="li-itemize">Programs include class and method definitions.</li><li class="li-itemize">Most of the computation is expressed in terms of operations on
objects.</li><li class="li-itemize">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 <span class="c004">Time</span> class defined in Chapter <a href="thinkpython2017.html#time">16</a>
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 <span class="c004">Point</span> and <span class="c004">Rectangle</span> classes
in Chapter <a href="thinkpython2016.html#clobjects">15</a>
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 <span class="c004">Time1.py</span> 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 <span class="c004">Time</span> object as an argument.
<a id="hevea_default1458"></a>
<a id="hevea_default1459"></a></p><p>This observation is the motivation for <span class="c010">methods</span>; 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.
<a id="hevea_default1460"></a>
<a id="hevea_default1461"></a>
<a id="hevea_default1462"></a>
<a id="hevea_default1463"></a></p><p>Methods are semantically the same as functions, but there are
two syntactic differences:</p><ul class="itemize"><li class="li-itemize">Methods are defined inside a class definition in order
to make the relationship between the class and the method explicit.</li><li class="li-itemize">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>
<h2 class="section" id="sec197">17.2  Printing objects</h2>
<p>
<a id="hevea_default1464"></a></p><p>In Chapter <a href="thinkpython2017.html#time">16</a>, we defined a class named
<span class="c004">Time</span> and in Section <a href="thinkpython2017.html#isafter">16.1</a>, you
wrote a function named <code>print_time</code>:</p><pre class="verbatim">class Time:
"""Represents the time of day."""
def print_time(time):
print('%.2d:%.2d:%.2d' % (time.hour, time.minute, time.second))
</pre><p>To call this function, you have to pass a <span class="c004">Time</span> object as an
argument:</p><pre class="verbatim">>>> start = Time()
>>> start.hour = 9
>>> start.minute = 45
>>> start.second = 00
>>> print_time(start)
09:45:00
</pre><p>To make <code>print_time</code> a method, all we have to do is
move the function definition inside the class definition. Notice
the change in indentation.
<a id="hevea_default1465"></a></p><pre class="verbatim">class Time:
def print_time(time):
print('%.2d:%.2d:%.2d' % (time.hour, time.minute, time.second))
</pre><p>Now there are two ways to call <code>print_time</code>. The first
(and less common) way is to use function syntax:
<a id="hevea_default1466"></a>
<a id="hevea_default1467"></a></p><pre class="verbatim">>>> Time.print_time(start)
09:45:00
</pre><p>In this use of dot notation, <span class="c004">Time</span> is the name of the class,
and <code>print_time</code> is the name of the method. <span class="c004">start</span> is
passed as a parameter.</p><p>The second (and more concise) way is to use method syntax:
<a id="hevea_default1468"></a></p><pre class="verbatim">>>> start.print_time()
09:45:00
</pre><p>In this use of dot notation, <code>print_time</code> is the name of the
method (again), and <span class="c004">start</span> is the object the method is
invoked on, which is called the <span class="c010">subject</span>. 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.
<a id="hevea_default1469"></a></p><p>Inside the method, the subject is assigned to the first
parameter, so in this case <span class="c004">start</span> is assigned
to <span class="c004">time</span>.
<a id="hevea_default1470"></a>
<a id="hevea_default1471"></a></p><p>By convention, the first parameter of a method is
called <span class="c004">self</span>, so it would be more common to write
<code>print_time</code> like this:</p><pre class="verbatim">class Time:
def print_time(self):
print('%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second))
</pre><p>The reason for this convention is an implicit metaphor:
<a id="hevea_default1472"></a></p><ul class="itemize"><li class="li-itemize">The syntax for a function call, <code>print_time(start)</code>,
suggests that the function is the active agent. It says something
like, “Hey <code>print_time</code>! Here’s an object for you to print.”</li><li class="li-itemize">In object-oriented programming, the objects are the active
agents. A method invocation like <code>start.print_time()</code> says
“Hey <span class="c004">start</span>! 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>time_to_int</code> (from
Section <a href="thinkpython2017.html#prototype">16.4</a>) as a method. You might be tempted to
rewrite <code>int_to_time</code> as a method, too, but that doesn’t
really make sense because there would be no object to invoke
it on.</p>
<h2 class="section" id="sec198">17.3  Another example</h2>
<p>
<a id="hevea_default1473"></a></p><p>Here’s a version of <span class="c004">increment</span> (from Section <a href="thinkpython2017.html#increment">16.3</a>)
rewritten as a method:</p><pre class="verbatim"># inside class Time:
def increment(self, seconds):
seconds += self.time_to_int()
return int_to_time(seconds)
</pre><p>This version assumes that <code>time_to_int</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 <span class="c004">increment</span>:</p><pre class="verbatim">>>> start.print_time()
09:45:00
>>> end = start.increment(1337)
>>> end.print_time()
10:07:17
</pre><p>The subject, <span class="c004">start</span>, gets assigned to the first parameter,
<span class="c004">self</span>. The argument, <span class="c004">1337</span>, gets assigned to the
second parameter, <span class="c004">seconds</span>.</p><p>This mechanism can be confusing, especially if you make an error.
For example, if you invoke <span class="c004">increment</span> with two arguments, you
get:
<a id="hevea_default1474"></a>
<a id="hevea_default1475"></a></p><pre class="verbatim">>>> end = start.increment(1337, 460)
TypeError: increment() takes 2 positional arguments but 3 were given
</pre><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 <span class="c010">positional argument</span> is an argument that
doesn’t have a parameter name; that is, it is not a keyword
argument. In this function call:
<a id="hevea_default1476"></a>
<a id="hevea_default1477"></a></p><pre class="verbatim">sketch(parrot, cage, dead=True)
</pre><p><span class="c004">parrot</span> and <span class="c004">cage</span> are positional, and <span class="c004">dead</span> is
a keyword argument.</p>
<h2 class="section" id="sec199">17.4  A more complicated example</h2>
<p>Rewriting <code>is_after</code> (from Section <a href="thinkpython2017.html#isafter">16.1</a>) is slightly
more complicated because it takes two Time objects as parameters. In
this case it is conventional to name the first parameter <span class="c004">self</span>
and the second parameter <span class="c004">other</span>: <a id="hevea_default1478"></a>
<a id="hevea_default1479"></a></p><pre class="verbatim"># inside class Time:
def is_after(self, other):
return self.time_to_int() > other.time_to_int()
</pre><p>To use this method, you have to invoke it on one object and pass
the other as an argument:</p><pre class="verbatim">>>> end.is_after(start)
True
</pre><p>One nice thing about this syntax is that it almost reads
like English: “end is after start?”</p>
<h2 class="section" id="sec200">17.5  The init method</h2>
<p>
<a id="hevea_default1480"></a>
<a id="hevea_default1481"></a></p><p>The init method (short for “initialization”) is
a special method that gets invoked when an object is instantiated.
Its full name is <code>__init__</code> (two underscore characters,
followed by <span class="c004">init</span>, and then two more underscores). An
init method for the <span class="c004">Time</span> class might look like this:</p><pre class="verbatim"># inside class Time:
def __init__(self, hour=0, minute=0, second=0):
self.hour = hour
self.minute = minute
self.second = second
</pre><p>It is common for the parameters of <code>__init__</code>
to have the same names as the attributes. The statement</p><pre class="verbatim"> self.hour = hour
</pre><p>stores the value of the parameter <span class="c004">hour</span> as an attribute
of <span class="c004">self</span>.
<a id="hevea_default1482"></a>
<a id="hevea_default1483"></a>
<a id="hevea_default1484"></a>
<a id="hevea_default1485"></a></p><p>The parameters are optional, so if you call <span class="c004">Time</span> with
no arguments, you get the default values.</p><pre class="verbatim">>>> time = Time()
>>> time.print_time()
00:00:00
</pre><p>If you provide one argument, it overrides <span class="c004">hour</span>:</p><pre class="verbatim">>>> time = Time (9)
>>> time.print_time()
09:00:00
</pre><p>If you provide two arguments, they override <span class="c004">hour</span> and
<span class="c004">minute</span>.</p><pre class="verbatim">>>> time = Time(9, 45)
>>> time.print_time()
09:45:00
</pre><p>And if you provide three arguments, they override all three
default values.</p><p>As an exercise, write an init method for the <span class="c004">Point</span> class that takes
<span class="c004">x</span> and <span class="c004">y</span> as optional parameters and assigns
them to the corresponding attributes.
<a id="hevea_default1486"></a>
<a id="hevea_default1487"></a></p>
<h2 class="section" id="sec201">17.6  The <span class="c004">__str__</span> method</h2>
<p>
<a id="hevea_default1488"></a>
<a id="hevea_default1489"></a></p><p><code>__str__</code> is a special method, like <code>__init__</code>,
that is supposed to return a string representation of an object.
<a id="hevea_default1490"></a></p><p>For example, here is a <span class="c004">str</span> method for Time objects:</p><pre class="verbatim"># inside class Time:
def __str__(self):
return '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second)
</pre><p>When you <span class="c004">print</span> an object, Python invokes the <span class="c004">str</span> method:
<a id="hevea_default1491"></a>
<a id="hevea_default1492"></a></p><pre class="verbatim">>>> time = Time(9, 45)
>>> print(time)
09:45:00
</pre><p>When I write a new class, I almost always start by writing
<code>__init__</code>, which makes it easier to instantiate objects, and
<code>__str__</code>, which is useful for debugging.</p><p>As an exercise, write a <span class="c004">str</span> method for the <span class="c004">Point</span> class.
Create a Point object and print it.</p>
<h2 class="section" id="sec202">17.7  Operator overloading</h2>
<p>
<a id="operator.overloading"></a></p><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>__add__</code> for the <span class="c004">Time</span> class, you can use the
<span class="c004">+</span> operator on Time objects.
<a id="hevea_default1493"></a>
<a id="hevea_default1494"></a></p><p>Here is what the definition might look like:
<a id="hevea_default1495"></a>
<a id="hevea_default1496"></a></p><pre class="verbatim"># inside class Time:
def __add__(self, other):
seconds = self.time_to_int() + other.time_to_int()
return int_to_time(seconds)
</pre><p>And here is how you could use it:</p><pre class="verbatim">>>> start = Time(9, 45)
>>> duration = Time(1, 35)
>>> print(start + duration)
11:20:00
</pre><p>When you apply the <span class="c004">+</span> operator to Time objects, Python invokes
<code>__add__</code>. When you print the result, Python invokes
<code>__str__</code>. So there is a lot happening behind the scenes!
<a id="hevea_default1497"></a></p><p>Changing the behavior of an operator so that it works with
programmer-defined types is called <span class="c010">operator overloading</span>. For every
operator in Python there is a corresponding special method, like
<code>__add__</code>. For more details, see
<a href="http://docs.python.org/3/reference/datamodel.html#specialnames"><span class="c004">http://docs.python.org/3/reference/datamodel.html#specialnames</span></a>.</p><p>As an exercise, write an <span class="c004">add</span> method for the Point class. </p>
<h2 class="section" id="sec203">17.8  Type-based dispatch</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>__add__</code>
that checks the type of <span class="c004">other</span> and invokes either
<code>add_time</code> or <span class="c004">increment</span>:</p><pre class="verbatim"># 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><p>The built-in function <span class="c004">isinstance</span> takes a value and a
class object, and returns <span class="c004">True</span> if the value is an instance
of the class.
<a id="hevea_default1498"></a>
<a id="hevea_default1499"></a></p><p>If <span class="c004">other</span> is a Time object, <code>__add__</code> invokes
<code>add_time</code>. Otherwise it assumes that the parameter
is a number and invokes <span class="c004">increment</span>. This operation is
called a <span class="c010">type-based dispatch</span> because it dispatches the
computation to different methods based on the type of the
arguments.
<a id="hevea_default1500"></a>
<a id="hevea_default1501"></a></p><p>Here are examples that use the <span class="c004">+</span> operator with different
types:</p><pre class="verbatim">>>> start = Time(9, 45)
>>> duration = Time(1, 35)
>>> print(start + duration)
11:20:00
>>> print(start + 1337)
10:07:17
</pre><p>Unfortunately, this implementation of addition is not commutative.
If the integer is the first operand, you get
<a id="hevea_default1502"></a></p><pre class="verbatim">>>> print(1337 + start)
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
</pre><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>__radd__</code>, which stands for “right-side add”.
This method is invoked when a Time object appears on the right side of
the <span class="c004">+</span> operator. Here’s the definition:
<a id="hevea_default1503"></a>
<a id="hevea_default1504"></a></p><pre class="verbatim"># inside class Time:
def __radd__(self, other):
return self.__add__(other)
</pre><p>And here’s how it’s used:</p><pre class="verbatim">>>> print(1337 + start)
10:07:17
</pre><p>As an exercise, write an <span class="c004">add</span> method for Points that works with
either a Point object or a tuple:</p><ul class="itemize"><li class="li-itemize">If the second operand is a Point, the method should return a new
Point whose <span class="c009">x</span> coordinate is the sum of the <span class="c009">x</span> coordinates of the
operands, and likewise for the <span class="c009">y</span> coordinates.</li><li class="li-itemize">If the second operand is a tuple, the method should add the
first element of the tuple to the <span class="c009">x</span> coordinate and the second
element to the <span class="c009">y</span> coordinate, and return a new Point with the result. </li></ul>
<h2 class="section" id="sec204">17.9  Polymorphism</h2>
<p>
<a id="polymorphism"></a></p><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.
<a id="hevea_default1505"></a>
<a id="hevea_default1506"></a></p><p>Many of the functions we wrote for strings also
work for other sequence types.
For example, in Section <a href="thinkpython2012.html#histogram">11.2</a>
we used <span class="c004">histogram</span> to count the number of times each letter
appears in a word.</p><pre class="verbatim">def histogram(s):
d = dict()
for c in s:
if c not in d:
d[c] = 1
else:
d[c] = d[c]+1
return d
</pre><p>This function also works for lists, tuples, and even dictionaries,
as long as the elements of <span class="c004">s</span> are hashable, so they can be used
as keys in <span class="c004">d</span>.</p><pre class="verbatim">>>> t = ['spam', 'egg', 'spam', 'spam', 'bacon', 'spam']
>>> histogram(t)
{'bacon': 1, 'egg': 1, 'spam': 4}
</pre><p>Functions that work with several types are called <span class="c010">polymorphic</span>.
Polymorphism can facilitate code reuse. For example, the built-in
function <span class="c004">sum</span>, which adds the elements of a sequence, works
as long as the elements of the sequence support addition.
<a id="hevea_default1507"></a></p><p>Since Time objects provide an <span class="c004">add</span> method, they work
with <span class="c004">sum</span>:</p><pre class="verbatim">>>> t1 = Time(7, 43)
>>> t2 = Time(7, 41)
>>> t3 = Time(7, 37)
>>> total = sum([t1, t2, t3])
>>> print(total)
23:01:00
</pre><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>
<h2 class="section" id="sec205">17.10  Debugging</h2>
<p>
<a id="hevea_default1508"></a></p><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.
<a id="hevea_default1509"></a>
<a id="hevea_default1510"></a></p><p>If you are not sure whether an object has a particular attribute, you
can use the built-in function <span class="c004">hasattr</span> (see Section <a href="thinkpython2016.html#hasattr">15.7</a>).
<a id="hevea_default1511"></a>
<a id="hevea_default1512"></a>
<a id="hevea_default1513"></a>
<a id="hevea_default1514"></a></p><p>Another way to access attributes is the built-in function <span class="c004">vars</span>,
which takes an object and returns a dictionary that maps from
attribute names (as strings) to their values:</p><pre class="verbatim">>>> p = Point(3, 4)
>>> vars(p)
{'y': 4, 'x': 3}
</pre><p>For purposes of debugging, you might find it useful to keep this
function handy:</p><pre class="verbatim">def print_attributes(obj):
for attr in vars(obj):
print(attr, getattr(obj, attr))
</pre><p><code>print_attributes</code> traverses the dictionary
and prints each attribute name and its corresponding value.
<a id="hevea_default1515"></a>
<a id="hevea_default1516"></a></p><p>The built-in function <span class="c004">getattr</span> takes an object and an attribute
name (as a string) and returns the attribute’s value.
<a id="hevea_default1517"></a>
<a id="hevea_default1518"></a></p>
<h2 class="section" id="sec206">17.11  Interface and implementation</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.
<a id="hevea_default1519"></a>
<a id="hevea_default1520"></a>
<a id="hevea_default1521"></a>
<a id="hevea_default1522"></a></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.
<a id="hevea_default1523"></a></p><p>For example, in this chapter we developed a class that represents
a time of day. Methods provided by this class include
<code>time_to_int</code>, <code>is_after</code>, and <code>add_time</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 <span class="c004">Time</span> object are <span class="c004">hour</span>, <span class="c004">minute</span>, and
<span class="c004">second</span>.</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>is_after</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>
<h2 class="section" id="sec207">17.12  Glossary</h2>
<dl class="description"><dt class="dt-description"><span class="c010">object-oriented language:</span></dt><dd class="dd-description"> A language that provides features,
such as programmer-defined types and methods, that facilitate
object-oriented programming.
<a id="hevea_default1524"></a></dd><dt class="dt-description"><span class="c010">object-oriented programming:</span></dt><dd class="dd-description"> A style of programming in which
data and the operations that manipulate it are organized into classes
and methods.
<a id="hevea_default1525"></a></dd><dt class="dt-description"><span class="c010">method:</span></dt><dd class="dd-description"> A function that is defined inside a class definition and
is invoked on instances of that class.
<a id="hevea_default1526"></a></dd><dt class="dt-description"><span class="c010">subject:</span></dt><dd class="dd-description"> The object a method is invoked on.
<a id="hevea_default1527"></a></dd><dt class="dt-description"><span class="c010">positional argument:</span></dt><dd class="dd-description"> An argument that does not include
a parameter name, so it is not a keyword argument.
<a id="hevea_default1528"></a>
<a id="hevea_default1529"></a></dd><dt class="dt-description"><span class="c010">operator overloading:</span></dt><dd class="dd-description"> Changing the behavior of an operator like
<span class="c004">+</span> so it works with a programmer-defined type.
<a id="hevea_default1530"></a>
<a id="hevea_default1531"></a></dd><dt class="dt-description"><span class="c010">type-based dispatch:</span></dt><dd class="dd-description"> A programming pattern that checks the type
of an operand and invokes different functions for different types.
<a id="hevea_default1532"></a></dd><dt class="dt-description"><span class="c010">polymorphic:</span></dt><dd class="dd-description"> Pertaining to a function that can work with more
than one type.
<a id="hevea_default1533"></a></dd><dt class="dt-description"><span class="c010">information hiding:</span></dt><dd class="dd-description"> The principle that the interface provided
by an object should not depend on its implementation, in particular
the representation of its attributes.
<a id="hevea_default1534"></a></dd></dl>
<h2 class="section" id="sec208">17.13  Exercises</h2>
<div class="theorem"><span class="c010">Exercise 1</span>  <p><em>Download the code from this chapter from
</em><a href="http://thinkpython2.com/code/Time2.py"><span class="c004"><em>http://thinkpython2.com/code/Time2.py</em></span></a><em>. Change the attributes of
<span class="c004">Time</span> to be a single integer representing seconds since
midnight. Then modify the methods (and the function
<code>int_to_time</code>) to work with the new implementation. You
should not have to modify the test code in <span class="c004">main</span>. When you
are done, the output should be the same as before. Solution:
</em><a href="http://thinkpython2.com/code/Time2_soln.py"><span class="c004"><em>http://thinkpython2.com/code/Time2_soln.py</em></span></a><em>.</em></p></div><div class="theorem"><span class="c010">Exercise 2</span>  
<a id="kangaroo"></a>
<a id="hevea_default1535"></a>
<a id="hevea_default1536"></a>
<a id="hevea_default1537"></a>
<a id="hevea_default1538"></a>
<a id="hevea_default1539"></a>
<a id="hevea_default1540"></a><p><em>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 <span class="c004">Kangaroo</span> with the following
methods:</em></p><ol class="enumerate" type=1><li class="li-enumerate"><em>An <code>__init__</code> method that initializes an attribute named
<code>pouch_contents</code> to an empty list.</em></li><li class="li-enumerate"><em>A method named <code>put_in_pouch</code> that takes an object
of any type and adds it to <code>pouch_contents</code>.</em></li><li class="li-enumerate"><em>A <code>__str__</code> method that returns a string representation
of the Kangaroo object and the contents of the pouch.</em></li></ol><p><em>
Test your code
by creating two <span class="c004">Kangaroo</span> objects, assigning them to variables
named <span class="c004">kanga</span> and <span class="c004">roo</span>, and then adding <span class="c004">roo</span> to the
contents of <span class="c004">kanga</span>’s pouch.</em></p><p><em>Download </em><a href="http://thinkpython2.com/code/BadKangaroo.py"><em><span class="c004">http://thinkpython2.com/code/BadKangaroo.py</span></em></a><em>. It contains
a solution to the previous problem with one big, nasty bug.
Find and fix the bug.</em></p><p><em>If you get stuck, you can download
</em><a href="http://thinkpython2.com/code/GoodKangaroo.py"><span class="c004"><em>http://thinkpython2.com/code/GoodKangaroo.py</em></span></a><em>, which explains the
problem and demonstrates a solution.
</em><a id="hevea_default1541"></a>
<a id="hevea_default1542"></a>
<a id="hevea_default1543"></a></p></div>
<p>
</td>
<td width=130 valign="top" id="col-right">
<p>
<h4>Are you using one of our books in a class?</h4> We'd like to know
about it. Please consider filling out <a href="http://spreadsheets.google.com/viewform?formkey=dC0tNUZkMjBEdXVoRGljNm9FRmlTMHc6MA" onClick="javascript: pageTracker._trackPageview('/outbound/survey');">this short survey</a>.
<p>
<br>
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491938455/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491938455&linkCode=as2&tag=greenteapre01-20&linkId=2JJH4SWCAVVYSQHO">Think DSP</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491938455" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491938455/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491938455&linkCode=as2&tag=greenteapre01-20&linkId=CTV7PDT7E5EGGJUM"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1491938455&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491938455" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491929561/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491929561&linkCode=as2&tag=greenteapre01-20&linkId=ZY6MAYM33ZTNSCNZ">Think Java</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491929561" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491929561/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491929561&linkCode=as2&tag=greenteapre01-20&linkId=PT77ANWARUNNU3UK"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1491929561&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491929561" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1449370780/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1449370780&linkCode=as2&tag=greenteapre01-20">Think Bayes</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1449370780" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1449370780/ref=as_li_qf_sp_asin_il?ie=UTF8&camp=1789&creative=9325&creativeASIN=1449370780&linkCode=as2&tag=greenteapre01-20"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1449370780&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1449370780" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491939362/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491939362&linkCode=as2&tag=greenteapre01-20&linkId=FJKSQ3IHEMY2F2VA">Think Python 2e</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491939362" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491939362/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491939362&linkCode=as2&tag=greenteapre01-20&linkId=ZZ454DLQ3IXDHNHX"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1491939362&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491939362" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1491907339/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491907339&linkCode=as2&tag=greenteapre01-20&linkId=O7WYM6H6YBYUFNWU">Think Stats 2e</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491907339" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1491907339/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491907339&linkCode=as2&tag=greenteapre01-20&linkId=JVSYKQHYSUIEYRHL"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1491907339&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491907339" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1449314635/ref=as_li_tf_tl?ie=UTF8&tag=greenteapre01-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1449314635">Think Complexity</a><img class="c003" src="http://www.assoc-amazon.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1449314635" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1449314635/ref=as_li_tf_il?ie=UTF8&camp=1789&creative=9325&creativeASIN=1449314635&linkCode=as2&tag=greenteapre01-20"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1449314635&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://www.assoc-amazon.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1449314635" width="1" height="1" border="0" alt="">
</td>
</tr>
</table>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#"><strong>Think Python</strong> - How to Think like a Computer Scientist (2e) <em>by Allen B. Downey</em></a>
</div>
<div>
<ul class="nav navbar-nav navbar-right">
<li><a href="http://greenteapress.com/thinkpython2/html/index.html"><span class="glyphicon glyphicon glyphicon-book" aria-hidden="true"></span></a></li>
<li><a href="thinkpython2017.html"><span class="glyphicon glyphicon glyphicon-menu-left" aria-hidden="true"></span></a></li>
<li><a href="index.html"><span class="glyphicon glyphicon glyphicon-home" aria-hidden="true"></span></a></li>
<li><a href="thinkpython2019.html"><span class="glyphicon glyphicon glyphicon-menu-right" aria-hidden="true"></span></a></li>
<li><a href="http://amzn.to/1VUYQUU"><span class="glyphicon glyphicon glyphicon-shopping-cart" aria-hidden="true"></span></a></li>
</ul>
<div>
</div><!-- /.container-fluid -->
</nav></body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
</html>