-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path11-dict.html
More file actions
710 lines (685 loc) · 46.5 KB
/
11-dict.html
File metadata and controls
710 lines (685 loc) · 46.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
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
<!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>Dictionaries — 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="Tuples" href="12-tuple.html" />
<link rel="prev" title="Lists" href="10-list.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="dictionaries">
<h1>Dictionaries<a class="headerlink" href="#dictionaries" title="Permalink to this headline">¶</a></h1>
<p>This chapter presents another built-in type called a dictionary.
Dictionaries are one of Python’s best features; they are the building
blocks of many efficient and elegant algorithms.</p>
<div class="section" id="a-dictionary-is-a-mapping">
<h2>A dictionary is a mapping<a class="headerlink" href="#a-dictionary-is-a-mapping" title="Permalink to this headline">¶</a></h2>
<p>A <strong>dictionary</strong> is like a list, but more general. In a list, the
indices have to be integers; in a dictionary they can be (almost) any
type.</p>
<p>A dictionary contains a collection of indices, which are called
<strong>keys</strong>, and a collection of values. Each key is associated with a
single value. The association of a key and a value is called a
<strong>key-value pair</strong> or sometimes an <strong>item</strong>.</p>
<p>In mathematical language, a dictionary represents a <strong>mapping</strong> from
keys to values, so you can also say that each key “maps to” a value. As
an example, we’ll build a dictionary that maps from English to Spanish
words, so the keys and the values are all strings.</p>
<p>The function dict creates a new dictionary with no items. Because dict
is the name of a built-in function, you should avoid using it as a
variable name.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">eng2sp</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">eng2sp</span>
<span class="go">{}</span>
</pre></div>
</div>
<p>The squiggly-brackets, <code class="docutils literal"><span class="pre">{}</span></code>, represent an empty dictionary. To add
items to the dictionary, you can use square brackets:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">eng2sp</span><span class="p">[</span><span class="s">'one'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'uno'</span>
</pre></div>
</div>
<p>This line creates an item that maps from the key <code class="docutils literal"><span class="pre">'one'</span></code> to the value
<code class="docutils literal"><span class="pre">'uno'</span></code>. If we print the dictionary again, we see a key-value pair
with a colon between the key and value:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">eng2sp</span>
<span class="go">{'one': 'uno'}</span>
</pre></div>
</div>
<p>This output format is also an input format. For example, you can create
a new dictionary with three items:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">eng2sp</span> <span class="o">=</span> <span class="p">{</span><span class="s">'one'</span><span class="p">:</span> <span class="s">'uno'</span><span class="p">,</span> <span class="s">'two'</span><span class="p">:</span> <span class="s">'dos'</span><span class="p">,</span> <span class="s">'three'</span><span class="p">:</span> <span class="s">'tres'</span><span class="p">}</span>
</pre></div>
</div>
<p>But if you print eng2sp, you might be surprised:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">eng2sp</span>
<span class="go">{'one': 'uno', 'three': 'tres', 'two': 'dos'}</span>
</pre></div>
</div>
<p>The order of the key-value pairs might not be the same. If you type the
same example on your computer, you might get a different result. In
general, the order of items in a dictionary is unpredictable.</p>
<p>But that’s not a problem because the elements of a dictionary are never
indexed with integer indices. Instead, you use the keys to look up the
corresponding values:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">eng2sp</span><span class="p">[</span><span class="s">'two'</span><span class="p">]</span>
<span class="go">'dos'</span>
</pre></div>
</div>
<p>The key <code class="docutils literal"><span class="pre">'two'</span></code> always maps to the value <code class="docutils literal"><span class="pre">'dos'</span></code> so the order of the
items doesn’t matter.</p>
<p>If the key isn’t in the dictionary, you get an exception:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">eng2sp</span><span class="p">[</span><span class="s">'four'</span><span class="p">]</span>
<span class="go">KeyError: 'four'</span>
</pre></div>
</div>
<p>The len function works on dictionaries; it returns the number of
key-value pairs:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="nb">len</span><span class="p">(</span><span class="n">eng2sp</span><span class="p">)</span>
<span class="go">3</span>
</pre></div>
</div>
<p>The in operator works on dictionaries, too; it tells you whether
something appears as a <em>key</em> in the dictionary (appearing as a value is
not good enough).</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="s">'one'</span> <span class="ow">in</span> <span class="n">eng2sp</span>
<span class="go">True</span>
<span class="gp">>>> </span><span class="s">'uno'</span> <span class="ow">in</span> <span class="n">eng2sp</span>
<span class="go">False</span>
</pre></div>
</div>
<p>To see whether something appears as a value in a dictionary, you can use
the method values, which returns a collection of values, and then use
the in operator:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">vals</span> <span class="o">=</span> <span class="n">eng2sp</span><span class="o">.</span><span class="n">values</span><span class="p">()</span>
<span class="gp">>>> </span><span class="s">'uno'</span> <span class="ow">in</span> <span class="n">vals</span>
<span class="go">True</span>
</pre></div>
</div>
<p>The in operator uses different algorithms for lists and dictionaries.
For lists, it searches the elements of the list in order, as in
Section [find]. As the list gets longer, the search time gets longer in
direct proportion.</p>
<p>For dictionaries, Python uses an algorithm called a <strong>hashtable</strong> that
has a remarkable property: the in operator takes about the same amount
of time no matter how many items are in the dictionary. I explain how
that’s possible in Section [hashtable], but the explanation might not
make sense until you’ve read a few more chapters.</p>
</div>
<div class="section" id="dictionary-as-a-collection-of-counters">
<h2>Dictionary as a collection of counters<a class="headerlink" href="#dictionary-as-a-collection-of-counters" title="Permalink to this headline">¶</a></h2>
<p>Suppose you are given a string and you want to count how many times each
letter appears. There are several ways you could do it:</p>
<ol class="arabic simple">
<li>You could create 26 variables, one for each letter of the alphabet.
Then you could traverse the string and, for each character, increment
the corresponding counter, probably using a chained conditional.</li>
<li>You could create a list with 26 elements. Then you could convert each
character to a number (using the built-in function ord), use the
number as an index into the list, and increment the appropriate
counter.</li>
<li>You could create a dictionary with characters as keys and counters as
the corresponding values. The first time you see a character, you
would add an item to the dictionary. After that you would increment
the value of an existing item.</li>
</ol>
<p>Each of these options performs the same computation, but each of them
implements that computation in a different way.</p>
<p>An <strong>implementation</strong> is a way of performing a computation; some
implementations are better than others. For example, an advantage of the
dictionary implementation is that we don’t have to know ahead of time
which letters appear in the string and we only have to make room for the
letters that do appear.</p>
<p>Here is what the code might look like:</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="mi">1</span>
<span class="k">return</span> <span class="n">d</span>
</pre></div>
</div>
<p>The name of the function is histogram, which is a statistical term for a
collection of counters (or frequencies).</p>
<p>The first line of the function creates an empty dictionary. The for loop
traverses the string. Each time through the loop, if the character c is
not in the dictionary, we create a new item with key c and the initial
value 1 (since we have seen this letter once). If c is already in the
dictionary we increment d[c].</p>
<p>Here’s how it works:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">h</span> <span class="o">=</span> <span class="n">histogram</span><span class="p">(</span><span class="s">'brontosaurus'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">h</span>
<span class="go">{'a': 1, 'b': 1, 'o': 2, 'n': 1, 's': 2, 'r': 2, 'u': 2, 't': 1}</span>
</pre></div>
</div>
<p>The histogram indicates that the letters <code class="docutils literal"><span class="pre">'a'</span></code> and <code class="docutils literal"><span class="pre">'b'</span></code> appear
once; <code class="docutils literal"><span class="pre">'o'</span></code> appears twice, and so on.</p>
<p>Dictionaries have a method called get that takes a key and a default
value. If the key appears in the dictionary, get returns the
corresponding value; otherwise it returns the default value. For
example:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">h</span> <span class="o">=</span> <span class="n">histogram</span><span class="p">(</span><span class="s">'a'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">h</span>
<span class="go">{'a': 1}</span>
<span class="gp">>>> </span><span class="n">h</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'a'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="go">1</span>
<span class="gp">>>> </span><span class="n">h</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s">'b'</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="go">0</span>
</pre></div>
</div>
<p>As an exercise, use get to write histogram more concisely. You should be
able to eliminate the if statement.</p>
</div>
<div class="section" id="looping-and-dictionaries">
<h2>Looping and dictionaries<a class="headerlink" href="#looping-and-dictionaries" title="Permalink to this headline">¶</a></h2>
<p>If you use a dictionary in a for statement, it traverses the keys of the
dictionary. For example, <code class="docutils literal"><span class="pre">print_hist</span></code> prints each key and the
corresponding value:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">print_hist</span><span class="p">(</span><span class="n">h</span><span class="p">):</span>
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">h</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="n">h</span><span class="p">[</span><span class="n">c</span><span class="p">])</span>
</pre></div>
</div>
<p>Here’s what the output looks like:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">h</span> <span class="o">=</span> <span class="n">histogram</span><span class="p">(</span><span class="s">'parrot'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">print_hist</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="go">a 1</span>
<span class="go">p 1</span>
<span class="go">r 2</span>
<span class="go">t 1</span>
<span class="go">o 1</span>
</pre></div>
</div>
<p>Again, the keys are in no particular order. To traverse the keys in
sorted order, you can use the built-in function sorted:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">h</span><span class="p">):</span>
<span class="gp">... </span> <span class="k">print</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">h</span><span class="p">[</span><span class="n">key</span><span class="p">])</span>
<span class="go">a 1</span>
<span class="go">o 1</span>
<span class="go">p 1</span>
<span class="go">r 2</span>
<span class="go">t 1</span>
</pre></div>
</div>
</div>
<div class="section" id="reverse-lookup">
<h2>Reverse lookup<a class="headerlink" href="#reverse-lookup" title="Permalink to this headline">¶</a></h2>
<p>Given a dictionary d and a key k, it is easy to find the corresponding
value v = d[k]. This operation is called a <strong>lookup</strong>.</p>
<p>But what if you have v and you want to find k? You have two problems:
first, there might be more than one key that maps to the value v.
Depending on the application, you might be able to pick one, or you
might have to make a list that contains all of them. Second, there is no
simple syntax to do a <strong>reverse lookup</strong>; you have to search.</p>
<p>Here is a function that takes a value and returns the first key that
maps to that value:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">reverse_lookup</span><span class="p">(</span><span class="n">d</span><span class="p">,</span> <span class="n">v</span><span class="p">):</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">d</span><span class="p">:</span>
<span class="k">if</span> <span class="n">d</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">==</span> <span class="n">v</span><span class="p">:</span>
<span class="k">return</span> <span class="n">k</span>
<span class="k">raise</span> <span class="ne">LookupError</span><span class="p">()</span>
</pre></div>
</div>
<p>This function is yet another example of the search pattern, but it uses
a feature we haven’t seen before, raise. The <strong>raise statement</strong> causes
an exception; in this case it causes a LookupError, which is a built-in
exception used to indicate that a lookup operation failed.</p>
<p>If we get to the end of the loop, that means v doesn’t appear in the
dictionary as a value, so we raise an exception.</p>
<p>Here is an example of a successful reverse lookup:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">h</span> <span class="o">=</span> <span class="n">histogram</span><span class="p">(</span><span class="s">'parrot'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">key</span> <span class="o">=</span> <span class="n">reverse_lookup</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">key</span>
<span class="go">'r'</span>
</pre></div>
</div>
<p>And an unsuccessful one:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">key</span> <span class="o">=</span> <span class="n">reverse_lookup</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
File <span class="nb">"<stdin>"</span>, line <span class="m">5</span>, in <span class="n">reverse_lookup</span>
<span class="gr">LookupError</span>
</pre></div>
</div>
<p>The effect when you raise an exception is the same as when Python raises
one: it prints a traceback and an error message.</p>
<p>The raise statement can take a detailed error message as an optional
argument. For example:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="k">raise</span> <span class="ne">LookupError</span><span class="p">(</span><span class="s">'value does not appear in the dictionary'</span><span class="p">)</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n">?</span>
<span class="gr">LookupError</span>: <span class="n">value does not appear in the dictionary</span>
</pre></div>
</div>
<p>A reverse lookup is much slower than a forward lookup; if you have to do
it often, or if the dictionary gets big, the performance of your program
will suffer.</p>
</div>
<div class="section" id="dictionaries-and-lists">
<h2>Dictionaries and lists<a class="headerlink" href="#dictionaries-and-lists" title="Permalink to this headline">¶</a></h2>
<p>Lists can appear as values in a dictionary. For example, if you are
given a dictionary that maps from letters to frequencies, you might want
to invert it; that is, create a dictionary that maps from frequencies to
letters. Since there might be several letters with the same frequency,
each value in the inverted dictionary should be a list of letters.</p>
<p>Here is a function that inverts a dictionary:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">invert_dict</span><span class="p">(</span><span class="n">d</span><span class="p">):</span>
<span class="n">inverse</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">d</span><span class="p">:</span>
<span class="n">val</span> <span class="o">=</span> <span class="n">d</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">if</span> <span class="n">val</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">inverse</span><span class="p">:</span>
<span class="n">inverse</span><span class="p">[</span><span class="n">val</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">inverse</span><span class="p">[</span><span class="n">val</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
<span class="k">return</span> <span class="n">inverse</span>
</pre></div>
</div>
<p>Each time through the loop, key gets a key from d and val gets the
corresponding value. If val is not in inverse, that means we haven’t
seen it before, so we create a new item and initialize it with a
<strong>singleton</strong> (a list that contains a single element). Otherwise we have
seen this value before, so we append the corresponding key to the list.</p>
<p>Here is an example:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">>>> </span><span class="n">hist</span> <span class="o">=</span> <span class="n">histogram</span><span class="p">(</span><span class="s">'parrot'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">hist</span>
<span class="go">{'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1}</span>
<span class="gp">>>> </span><span class="n">inverse</span> <span class="o">=</span> <span class="n">invert_dict</span><span class="p">(</span><span class="n">hist</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">inverse</span>
<span class="go">{1: ['a', 'p', 't', 'o'], 2: ['r']}</span>
</pre></div>
</div>
<div class="figure" id="id1">
<img alt="State diagram." src="_images/dict1.pdf" />
<p class="caption"><span class="caption-text">State diagram.</span></p>
</div>
<p>Figure [fig.dict1] is a state diagram showing hist and inverse. A
dictionary is represented as a box with the type dict above it and the
key-value pairs inside. If the values are integers, floats or strings, I
draw them inside the box, but I usually draw lists outside the box, just
to keep the diagram simple.</p>
<p>Lists can be values in a dictionary, as this example shows, but they
cannot be keys. Here’s what happens if you try:</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="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="gp">>>> </span><span class="n">d</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">d</span><span class="p">[</span><span class="n">t</span><span class="p">]</span> <span class="o">=</span> <span class="s">'oops'</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n">?</span>
<span class="gr">TypeError</span>: <span class="n">list objects are unhashable</span>
</pre></div>
</div>
<p>I mentioned earlier that a dictionary is implemented using a hashtable
and that means that the keys have to be <strong>hashable</strong>.</p>
<p>A <strong>hash</strong> is a function that takes a value (of any kind) and returns an
integer. Dictionaries use these integers, called hash values, to store
and look up key-value pairs.</p>
<p>This system works fine if the keys are immutable. But if the keys are
mutable, like lists, bad things happen. For example, when you create a
key-value pair, Python hashes the key and stores it in the corresponding
location. If you modify the key and then hash it again, it would go to a
different location. In that case you might have two entries for the same
key, or you might not be able to find a key. Either way, the dictionary
wouldn’t work correctly.</p>
<p>That’s why keys have to be hashable, and why mutable types like lists
aren’t. The simplest way to get around this limitation is to use tuples,
which we will see in the next chapter.</p>
<p>Since dictionaries are mutable, they can’t be used as keys, but they
<em>can</em> be used as values.</p>
</div>
<div class="section" id="memos">
<h2>Memos<a class="headerlink" href="#memos" title="Permalink to this headline">¶</a></h2>
<p>If you played with the fibonacci function from
Section [one.more.example], you might have noticed that the bigger the
argument you provide, the longer the function takes to run. Furthermore,
the run time increases quickly.</p>
<p>To understand why, consider Figure [fig.fibonacci], which shows the
<strong>call graph</strong> for fibonacci with n=4:</p>
<div class="figure" id="id2">
<img alt="Call graph." src="_images/fibonacci.pdf" />
<p class="caption"><span class="caption-text">Call graph.</span></p>
</div>
<p>A call graph shows a set of function frames, with lines connecting each
frame to the frames of the functions it calls. At the top of the graph,
fibonacci with n=4 calls fibonacci with n=3 and n=2. In turn, fibonacci
with n=3 calls fibonacci with n=2 and n=1. And so on.</p>
<p>Count how many times fibonacci(0) and fibonacci(1) are called. This is
an inefficient solution to the problem, and it gets worse as the
argument gets bigger.</p>
<p>One solution is to keep track of values that have already been computed
by storing them in a dictionary. A previously computed value that is
stored for later use is called a <strong>memo</strong>. Here is a “memoized” version
of fibonacci:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">known</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">:</span><span class="mi">1</span><span class="p">}</span>
<span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<span class="k">if</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">known</span><span class="p">:</span>
<span class="k">return</span> <span class="n">known</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>
<span class="n">res</span> <span class="o">=</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="n">known</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="n">res</span>
<span class="k">return</span> <span class="n">res</span>
</pre></div>
</div>
<p>known is a dictionary that keeps track of the Fibonacci numbers we
already know. It starts with two items: 0 maps to 0 and 1 maps to 1.</p>
<p>Whenever fibonacci is called, it checks known. If the result is already
there, it can return immediately. Otherwise it has to compute the new
value, add it to the dictionary, and return it.</p>
<p>If you run this version of fibonacci and compare it with the original,
you will find that it is much faster.</p>
</div>
<div class="section" id="global-variables">
<h2>Global variables<a class="headerlink" href="#global-variables" title="Permalink to this headline">¶</a></h2>
<p>In the previous example, known is created outside the function, so it
belongs to the special frame called <code class="docutils literal"><span class="pre">__main__</span></code>. Variables in
<code class="docutils literal"><span class="pre">__main__</span></code> are sometimes called <strong>global</strong> because they can be
accessed from any function. Unlike local variables, which disappear when
their function ends, global variables persist from one function call to
the next.</p>
<p>It is common to use global variables for <strong>flags</strong>; that is, boolean
variables that indicate (“flag”) whether a condition is true. For
example, some programs use a flag named verbose to control the level of
detail in the output:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">verbose</span> <span class="o">=</span> <span class="bp">True</span>
<span class="k">def</span> <span class="nf">example1</span><span class="p">():</span>
<span class="k">if</span> <span class="n">verbose</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Running example1'</span><span class="p">)</span>
</pre></div>
</div>
<p>If you try to reassign a global variable, you might be surprised. The
following example is supposed to keep track of whether the function has
been called:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">been_called</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">def</span> <span class="nf">example2</span><span class="p">():</span>
<span class="n">been_called</span> <span class="o">=</span> <span class="bp">True</span> <span class="c"># WRONG</span>
</pre></div>
</div>
<p>But if you run it you will see that the value of <code class="docutils literal"><span class="pre">been_called</span></code> doesn’t
change. The problem is that example2 creates a new local variable named
<code class="docutils literal"><span class="pre">been_called</span></code>. The local variable goes away when the function ends,
and has no effect on the global variable.</p>
<p>To reassign a global variable inside a function you have to <strong>declare</strong>
the global variable before you use it:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">been_called</span> <span class="o">=</span> <span class="bp">False</span>
<span class="k">def</span> <span class="nf">example2</span><span class="p">():</span>
<span class="k">global</span> <span class="n">been_called</span>
<span class="n">been_called</span> <span class="o">=</span> <span class="bp">True</span>
</pre></div>
</div>
<p>The <strong>global statement</strong> tells the interpreter something like, “In this
function, when I say <code class="docutils literal"><span class="pre">been_called</span></code>, I mean the global variable; don’t
create a local one.”</p>
<p>Here’s an example that tries to update a global variable:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">example3</span><span class="p">():</span>
<span class="n">count</span> <span class="o">=</span> <span class="n">count</span> <span class="o">+</span> <span class="mi">1</span> <span class="c"># WRONG</span>
</pre></div>
</div>
<p>If you run it you get:</p>
<div class="highlight-python"><div class="highlight"><pre>UnboundLocalError: local variable 'count' referenced before assignment
</pre></div>
</div>
<p>Python assumes that count is local, and under that assumption you are
reading it before writing it. The solution, again, is to declare count
global.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">example3</span><span class="p">():</span>
<span class="k">global</span> <span class="n">count</span>
<span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
</pre></div>
</div>
<p>If a global variable refers to a mutable value, you can modify the value
without declaring the variable:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">known</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">:</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">:</span><span class="mi">1</span><span class="p">}</span>
<span class="k">def</span> <span class="nf">example4</span><span class="p">():</span>
<span class="n">known</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
</pre></div>
</div>
<p>So you can add, remove and replace elements of a global list or
dictionary, but if you want to reassign the variable, you have to
declare it:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">example5</span><span class="p">():</span>
<span class="k">global</span> <span class="n">known</span>
<span class="n">known</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
</pre></div>
</div>
<p>Global variables can be useful, but if you have a lot of them, and you
modify them frequently, they can make programs hard to debug.</p>
</div>
<div class="section" id="debugging">
<h2>Debugging<a class="headerlink" href="#debugging" title="Permalink to this headline">¶</a></h2>
<p>As you work with bigger datasets it can become unwieldy to debug by
printing and checking the output by hand. Here are some suggestions for
debugging large datasets:</p>
<dl class="docutils">
<dt>Scale down the input:</dt>
<dd><p class="first">If possible, reduce the size of the dataset. For example if the
program reads a text file, start with just the first 10 lines, or
with the smallest example you can find. You can either edit the
files themselves, or (better) modify the program so it reads only
the first n lines.</p>
<p class="last">If there is an error, you can reduce n to the smallest value that
manifests the error, and then increase it gradually as you find and
correct errors.</p>
</dd>
<dt>Check summaries and types:</dt>
<dd><p class="first">Instead of printing and checking the entire dataset, consider
printing summaries of the data: for example, the number of items in
a dictionary or the total of a list of numbers.</p>
<p class="last">A common cause of runtime errors is a value that is not the right
type. For debugging this kind of error, it is often enough to print
the type of a value.</p>
</dd>
<dt>Write self-checks:</dt>
<dd><p class="first">Sometimes you can write code to check for errors automatically. For
example, if you are computing the average of a list of numbers, you
could check that the result is not greater than the largest element
in the list or less than the smallest. This is called a “sanity
check” because it detects results that are “insane”.</p>
<p class="last">Another kind of check compares the results of two different
computations to see if they are consistent. This is called a
“consistency check”.</p>
</dd>
<dt>Format the output:</dt>
<dd>Formatting debugging output can make it easier to spot an error. We
saw an example in Section [factdebug]. The pprint module provides a
pprint function that displays built-in types in a more
human-readable format (pprint stands for “pretty print”).</dd>
</dl>
<p>Again, time you spend building scaffolding can reduce the time you spend
debugging.</p>
</div>
<div class="section" id="glossary">
<span id="glossary11"></span><h2>Glossary<a class="headerlink" href="#glossary" title="Permalink to this headline">¶</a></h2>
<dl class="docutils">
<dt>mapeamento (<em>mapping</em>)</dt>
<dd>A relationship in which each element of one set corresponds to an element of another set.</dd>
<dt>dicionário (<em>dictionary</em>)</dt>
<dd>A mapping from keys to their corresponding values.</dd>
<dt>par chave-valor (<em>key-value pair</em>)</dt>
<dd>The representation of the mapping from a key to a value.</dd>
<dt><em>item</em> (item)</dt>
<dd>In a dictionary, another name for a key-value pair.</dd>
<dt>chave (<em>key</em>)</dt>
<dd>An object that appears in a dictionary as the first part of a key-value pair.</dd>
<dt>valor (<em>value</em>)</dt>
<dd>An object that appears in a dictionary as the second part of a key-value pair. This is more specific than our previous use of the word “value”.</dd>
<dt>implementação (<em>implementation</em>)</dt>
<dd>A way of performing a computation.</dd>
<dt>tabela de hash (<em>hashtable</em>)</dt>
<dd>The algorithm used to implement Python dictionaries.</dd>
<dt>função de hash (<em>hash function</em>)</dt>
<dd>A function used by a hashtable to compute the location for a key.</dd>
<dt><em>hashable</em> ()</dt>
<dd>A type that has a hash function. Immutable types like integers, floats and strings are hashable; mutable types like lists and dictionaries are not.</dd>
<dt>busca (<em>lookup</em>)</dt>
<dd>A dictionary operation that takes a key and finds the corresponding value.</dd>
<dt>busca invertida (<em>reverse lookup</em>)</dt>
<dd>A dictionary operation that takes a value and finds one or more keys that map to it.</dd>
<dt><code class="docutils literal"><span class="pre">raise</span></code>, instrução (<code class="docutils literal"><span class="pre">raise</span></code> <em>statement</em>)</dt>
<dd>A statement that (deliberately) raises an exception.</dd>
<dt><em>singleton</em></dt>
<dd>A list (or other sequence) with a single element.</dd>
<dt>diagrama de chamadas (<em>call graph</em>)</dt>
<dd>A diagram that shows every frame created during the execution of a program, with an arrow from each caller to each callee.</dd>
<dt><em>memo</em> (“lembrete”)</dt>
<dd>A computed value stored to avoid unnecessary future computation.</dd>
<dt>variável global (<em>global variable</em>)</dt>
<dd>A variable defined outside a function. Global variables can be accessed from any function.</dd>
<dt><code class="docutils literal"><span class="pre">global</span></code>, declaração (<code class="docutils literal"><span class="pre">global</span></code> <em>statement</em>)</dt>
<dd>A statement that declares a variable name global.</dd>
<dt><em>flag</em> (“indicador”)</dt>
<dd>A boolean variable used to indicate whether a condition is true.</dd>
<dt>declaração (<em>declaration</em>)</dt>
<dd>A statement like global that tells the interpreter something about a variable.</dd>
</dl>
</div>
<div class="section" id="exercises">
<h2>Exercises<a class="headerlink" href="#exercises" title="Permalink to this headline">¶</a></h2>
<p>[wordlist2]</p>
<p>Write a function that reads the words in words.txt and stores them as
keys in a dictionary. It doesn’t matter what the values are. Then you
can use the in operator as a fast way to check whether a string is in
the dictionary.</p>
<p>If you did Exercise [wordlist1], you can compare the speed of this
implementation with the list in operator and the bisection search.</p>
<p>[setdefault]</p>
<p>Read the documentation of the dictionary method setdefault and use it to
write a more concise version of <code class="docutils literal"><span class="pre">invert_dict</span></code>. Solution:
<a class="reference external" href="http://thinkpython2.com/code/invert_dict.py">http://thinkpython2.com/code/invert_dict.py</a>.</p>
<p>Memoize the Ackermann function from Exercise [ackermann] and see if
memoization makes it possible to evaluate the function with bigger
arguments. Hint: no. Solution:
<a class="reference external" href="http://thinkpython2.com/code/ackermann_memo.py">http://thinkpython2.com/code/ackermann_memo.py</a>.</p>
<p>If you did Exercise [duplicate], you already have a function named
<code class="docutils literal"><span class="pre">has_duplicates</span></code> that takes a list as a parameter and returns True if
there is any object that appears more than once in the list.</p>
<p>Use a dictionary to write a faster, simpler version of
<code class="docutils literal"><span class="pre">has_duplicates</span></code>. Solution:
<a class="reference external" href="http://thinkpython2.com/code/has_duplicates.py">http://thinkpython2.com/code/has_duplicates.py</a>.</p>
<p>[exrotatepairs]</p>
<p>Two words are “rotate pairs” if you can rotate one of them and get the
other (see <code class="docutils literal"><span class="pre">rotate_word</span></code> in Exercise [exrotate]).</p>
<p>Write a program that reads a wordlist and finds all the rotate pairs.
Solution: <a class="reference external" href="http://thinkpython2.com/code/rotate_pairs.py">http://thinkpython2.com/code/rotate_pairs.py</a>.</p>
<p>Here’s another Puzzler from <em>Car Talk</em>
(<a class="reference external" href="http://www.cartalk.com/content/puzzlers">http://www.cartalk.com/content/puzzlers</a>):</p>
<blockquote>
<div><p>This was sent in by a fellow named Dan O’Leary. He came upon a
common one-syllable, five-letter word recently that has the
following unique property. When you remove the first letter, the
remaining letters form a homophone of the original word, that is a
word that sounds exactly the same. Replace the first letter, that
is, put it back and remove the second letter and the result is yet
another homophone of the original word. And the question is, what’s
the word?</p>
<p>Now I’m going to give you an example that doesn’t work. Let’s look
at the five-letter word, ‘wrack.’ W-R-A-C-K, you know like to ‘wrack
with pain.’ If I remove the first letter, I am left with a
four-letter word, ’R-A-C-K.’ As in, ‘Holy cow, did you see the rack
on that buck! It must have been a nine-pointer!’ It’s a perfect
homophone. If you put the ‘w’ back, and remove the ‘r,’ instead,
you’re left with the word, ‘wack,’ which is a real word, it’s just
not a homophone of the other two words.</p>
<p>But there is, however, at least one word that Dan and we know of,
which will yield two homophones if you remove either of the first
two letters to make two, new four-letter words. The question is,
what’s the word?</p>
</div></blockquote>
<p>You can use the dictionary from Exercise [wordlist2] to check whether a
string is in the word list.</p>
<p>To check whether two words are homophones, you can use the CMU
Pronouncing Dictionary. You can download it from
<a class="reference external" href="http://www.speech.cs.cmu.edu/cgi-bin/cmudict">http://www.speech.cs.cmu.edu/cgi-bin/cmudict</a> or from
<a class="reference external" href="http://thinkpython2.com/code/c06d">http://thinkpython2.com/code/c06d</a> and you can also download
<a class="reference external" href="http://thinkpython2.com/code/pronounce.py">http://thinkpython2.com/code/pronounce.py</a>, which provides a function
named <code class="docutils literal"><span class="pre">read_dictionary</span></code> that reads the pronouncing dictionary and
returns a Python dictionary that maps from each word to a string that
describes its primary pronunciation.</p>
<p>Write a program that lists all the words that solve the Puzzler.
Solution: <a class="reference external" href="http://thinkpython2.com/code/homophone.py">http://thinkpython2.com/code/homophone.py</a>.</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="#">Dictionaries</a><ul>
<li><a class="reference internal" href="#a-dictionary-is-a-mapping">A dictionary is a mapping</a></li>
<li><a class="reference internal" href="#dictionary-as-a-collection-of-counters">Dictionary as a collection of counters</a></li>
<li><a class="reference internal" href="#looping-and-dictionaries">Looping and dictionaries</a></li>
<li><a class="reference internal" href="#reverse-lookup">Reverse lookup</a></li>
<li><a class="reference internal" href="#dictionaries-and-lists">Dictionaries and lists</a></li>
<li><a class="reference internal" href="#memos">Memos</a></li>
<li><a class="reference internal" href="#global-variables">Global variables</a></li>
<li><a class="reference internal" href="#debugging">Debugging</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="10-list.html" title="previous chapter">Lists</a></li>
<li>Next: <a href="12-tuple.html" title="next chapter">Tuples</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/11-dict.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/11-dict.txt"
rel="nofollow">Page source</a>
</div>
</body>
</html>