-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.html
1190 lines (826 loc) · 37.8 KB
/
index.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
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=1024, user-scalable=no">
<title>Intermediate Python</title>
<!-- Required stylesheet -->
<link rel="stylesheet" media="screen" href="core/deck.core.css">
<!-- Extension CSS files go here. Remove or add as needed. -->
<link rel="stylesheet" media="screen" href="extensions/goto/deck.goto.css">
<link rel="stylesheet" media="screen" href="extensions/status/deck.status.css">
<link rel="stylesheet" media="screen" href="extensions/scale/deck.scale.css">
<!-- Style theme. More available in /themes/style/ or create your own. -->
<link rel="stylesheet" media="screen" href="themes/style/python.css">
<!-- Transition theme. More available in /themes/transition/ or create your own. -->
<link rel="stylesheet" media="screen" href="themes/transition/fade.css">
<!-- Basic black and white print styles -->
<link rel="stylesheet" media="print" href="core/print.css">
<!-- Required Modernizr file -->
<script src="modernizr.custom.js"></script>
<link rel="stylesheet" href="highlight/styles/python.css">
</head>
<body>
<div class="deck-container">
<section class="slide slide-title">
<div class="slide-title-title">
<div class="slide-title-group">
<div class="slide-title-logo"></div>
<h2>Intermediate Python</h2>
</div>
<h4>Why You Too Should Love Python</h4>
</div>
<div class="slide-title-byline">
<h3>By <strong>Bailey Parker</strong></h3>
</div>
</section>
<section class="slide">
<h2>Pythonic Code</h2>
<p>As a preview of what's to come, here is how you would check if a number is in
a range in any other language:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">if 3 <= num and num < 10:
pass</code></pre>
<p>And here's how you do it in Python:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">if 3 <= num < 10:
pass</code></pre>
</section>
<section class="slide">
<h2>Tuples</h2>
<p>Tuples are immutable lists. Once they are created, they can't be changed. They
are used <em>everywhere</em> in Python.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> exam = ('Final Exam', 91)
>>> exam[0]
'Final Exam'
>>> exam[1]
91
# Try to change our grade
>>> exam[1] = 100
exception: 'tuple' object does not support item assignment
# A tuple with one element
>>> singleton = ('one element',)
</code></pre>
<p>Tuples can be returned from functions.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def compute_statistics(nums):
return average(nums), max(nums), stddev(nums)</code></pre>
</section>
<section class="slide">
<h3>Tuples: <span class="child-header">Unpacking</span></h3>
<p>Tuples can also be <strong>unpacked</strong> into separate variables.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> name, grade = exam
>>> name
'Final Exam'
>>> grade
91
</code></pre>
<p>You can also <strong>unpack</strong> multiple layers of tuples (or any iterable).</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> player = ('Shaquille', 'O\'Neal', (7, 1))
>>> first_name, last_name, (feet, inches) = player
>>> first_name
'Shaquille'
>>> last_name
"O'Neal"
>>> feet
7
>>> inches
1
</code></pre>
</section>
<section class="slide">
<h3>Tuples: <span class="child-header">An Aside on Naming</span></h3>
<p>Tuples are often convenient ways of quickly packaging related values together.
However, indexing them by number (ex. <code>exam[0]</code>) may be confusing. Instead, we
can name a tuple's elements by using <code>namedtuple</code>.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">from collections import namedtuple
Height = namedtuple('Height', 'feet inches')
Player = namedtuple('Player', 'first_name last_name height')</code></pre>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> player = Player('Shaquille', 'O\'Neal', Height(7, 1))
>>> player.first_name
'Shaquille'
>>> player.height.feet
7
# namedtuples behave like regular tuples (so they can be unpacked)
>>> first_name, last_name, height = player
>>> last_name
"O'Neal"
>>> height.inches
1
</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2>Strings</h2>
<p>Python has many commonly used string manipulation functions.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> names = 'Foo, Bar, Baz'
>>> names.split(', ')
['Foo', 'Bar', 'Baz']
>>> ' and '.join(names.split(', '))
'Foo and Bar and Baz'
>>> time_line = 'Time: 23.4'
>>> time_line.startswith('Time: ')
True
# Combining with unpacking provides a powerful way to parse strings
>>> _, seconds = time_line.split(': ')
>>> seconds = float(seconds)
>>> seconds
23.4
</code></pre>
</section>
<section class="slide">
<h3>Strings: <span class="child-header">Formatting</span></h3>
<p>Python 3.6 introduced f-strings, a convenient way to interpolate variables into
strings.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> name = 'Forrest Gump'
>>> birth_year = 1944
>>> skills = ['ping pong', 'running']
>>> f'{name} was born in {birth_year} and is good at: {skills}'
"Forrest Gump was born in 1944 and is good at: ['ping pong', 'running']"
</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2>Fun with Arguments</h2>
<p>Arguments can be given default values.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def discount_price(price, discount=0.5):
return price * discount</code></pre>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> discount_price(10)
5.0
>>> discount_price(10, 0.75)
7.5
</code></pre>
</section>
<section class="slide">
<h3>Fun with Arguments: <span class="child-header">Default Arguments</span></h3>
<p>Just be careful! Default arguments are only evaluated once (when the function
is defined), <em>not</em> at every function call.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def record_discount(discount, previous_discounts=[]):
previous_discounts.append(discount)
return previous_discounts</code></pre>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> record_discount(0.5)
[0.5]
>>> record_discount(0.75)
[0.5, 0.75]
</code></pre>
<p>The better approach uses <code>None</code> instead:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def record_discount(discount, previous_discounts=None):
if previous_discounts is None:
previous_discounts = []
previous_discounts.append(discount)
return previous_discounts</code></pre>
</section>
<section class="slide">
<h3>Fun with Arguments: <span class="child-header">Naming Arguments</span></h3>
<p>Arguments can and should be passed by name to reduce ambiguity. Compare the
following:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python"># What do these parameters mean?
search_tweets('@SwiftOnSecurity', 20, True, False)
# Much more clear...
search_tweets('@SwiftOnSecurity', retweets=False, popular=True, limit=20)</code></pre>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def search_tweets(query, limit, popular, retweets=True):
pass</code></pre>
<p>Using names makes your code more clear and also doesn't require you to remember
parameter order!</p>
</section>
<section class="slide">
<h3>Fun with Arguments: <span class="child-header">Requiring Argument Names</span></h3>
<p>You can even require that arguments be passed by name by using <code>*</code>.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def search_tweets(query, *, limit, popular, retweets=True):
pass
search_tweets('@SwiftOnSecurity', 20, True, False)</code></pre>
<pre><div class="pre-name oneline"><span>output</span></div><code>search_tweets() takes 1 positional argument but 4 were given</code></pre>
</section>
<section class="slide">
<h3>Fun with Arguments: <span class="child-header">Making a <code>sum</code> Function</span></h3>
<p>Let's use our newfound knowledge of arguments to make a function that sums its
arguments.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">sum(1, 2, 3) # 6
sum(0) # 0</code></pre>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def sum(start, *nums):
total = start
for num in nums:
total += num
return total</code></pre>
<p>This new <code>*</code> syntax allows us to collect any extra arguments passed to the
function. We can also use it to expand iterables as if they were passed as
individual arguments.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">nums = [1, 2, 3]
sum(*nums)
# is the same as...
sum(1, 2, 3)</code></pre>
<p>This syntax even works for tuple unpacking!</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> first, *rest = [1, 2, 3]
>>> first
1
>>> rest
[2, 3]
</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2>Functional Tools</h2>
<p>In fact, Python already has a built-in <code>sum</code> function that is much more
powerful.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> sum([1, 2, 3])
6
# The second parameter is the starting value to add values to
>>> sum([[1, 2, 3], [4, 5, 6]], [])
[1, 2, 3, 4, 5, 6]
</code></pre>
<p>Python provides many more functional tools:</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> min([10, 2, 22, 15])
2
>>> max([10, 2, 22, 15])
22
>>> sorted([10, 2, 22, 15])
[2, 10, 15, 22]
>>> sorted([10, 2, 22, 15], reverse=True)
[22, 15, 10, 2]
</code></pre>
</section>
<section class="slide">
<h3>Functional Tools: <span class="child-header">On Richer Values</span></h3>
<p>All of these functional tools allow you to specify a <code>key</code> which tells the
function what value to compare.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> Graded = namedtuple('Graded', 'name grade')
>>> grades = [Graded('Midterm', 80), Graded('Final', 90), Graded('Project', 0)]
>>> min(grades, key=lambda x: x.grade)
Graded(name='Project', grade=0)
>>> max(grades, key=lambda x: x.grade)
Graded(name='Final', grade=90)
>>> sorted(grades, key=lambda x: x.grade, reverse=True)
[Graded(name='Final', grade=90), Graded(name='Midterm', grade=80), Graded(name='Project', grade=0)]
</code></pre>
<p><code>lambda</code>s are convenient ways of expressing one line functions. The above one
is equivalent to:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def get_grade(x):
return x.grade</code></pre>
</section>
<section class="slide">
<h3>Functional Tools: <span class="child-header">Iteration</span></h3>
<p>If you were to port iteration over a list from another language to Python, you
may arrive at:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">for i in range(len(nums)):
print(nums[i])</code></pre>
<p>But we don't like explicit indices, because they are error prone. Instead, we
can just iterate over the list itself:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">for num in nums:
print(num)</code></pre>
<p>If we need the index, we use <code>enumerate</code>:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">for i, num in enumerate(nums):
print(i, num)</code></pre>
</section>
<section class="slide">
<h3>Functional Tools: <span class="child-header">Iteration of Multiple Lists</span></h3>
<p>What if we want to consider values from two lists at once? You may be inclined
to write:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">for i, name in enumerate(students):
print(f'{name} got a {grades[i]} in the class')</code></pre>
<p>Instead, we use <code>zip</code> to pair values from iterables together:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">for name, grade in zip(students, grades):
print(f'{name} got a {grade} in the class')</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2>Iterables</h2>
<p>Python is built around the idea of iterables and provides many standard library
and language features for interacting with and creating them.</p>
<p>We've already seen that <code>list</code> is an iterable and <code>dict</code> is an iterable, which
also provides other ways of iteration (<code>.items()</code> and <code>.values()</code>). Most
collections in Python are iterables. Even files are iterables!</p>
<p>We know how to iterate through collections, but let's see how we can build our
own iterables.</p>
</section>
<section class="slide">
<h3>Iterables: <span class="child-header">Generators</span></h3>
<p>Generators are a powerful tool for building efficient iterables. Consider
replicating Python's <code>range()</code>:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def our_range(stop):
num = 0
nums = []
while num < stop:
print(f'adding {num}')
nums.append(num)
num += 1
return nums</code></pre>
<p>What's the problem with this approach?</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">for num in our_range(3):
print(f'received {num}')</code></pre>
<pre><div class="pre-name oneline"><span>output</span></div><code>adding 0
adding 1
adding 2
received 0
received 1
received 2
</code></pre>
</section>
<section class="slide">
<h3>Iterables: Generators: <span class="child-header">Deferred Computation</span></h3>
<p>Generators use the <code>yield</code> keyword to stop their execution and hand a value
to the iterator's consumer.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def our_range(stop):
num = 0
while num < stop:
print(f'yielding {num}')
yield num
num += 1</code></pre>
<p>Now, the numbers are generated on the fly:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">for num in our_range(3):
print(f'received {num}')</code></pre>
<pre><div class="pre-name oneline"><span>output</span></div><code>yielding 0
received 0
yielding 1
received 1
yielding 2
received 2
</code></pre>
</section>
<section class="slide">
<h3>Iterables: <span class="child-header">Expressions</span></h3>
<p>This syntax can be expanded to create terse expressions of <code>list</code>s, <code>dict</code>s,
and even generators.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> [x * 2 for x in range(4)]
[0, 2, 4, 6]
>>> sum(x * 2 for x in range(4))
12
>>> {x: x * 2 for x in range(4)}
{0: 0, 1: 2, 2: 4, 3: 6}
</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2>OOP</h2>
<p>In many ways, object orientation in Python looks exactly like other languages
you may be familiar with like Java. There are a few subtle differences, though.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">class Course:
def __init__(self, number, name, instructor):
self.number = number
self.name = name
self.instructor = instructor
self._student_grades = {}
def add_student_grade(self, student, grade):
self._student_grades[student] = grade
def curve(self, amount):
for student, grade in self._student_grades.items():
self.add_student_grade(student, grade + amount)</code></pre>
<p>Notably, Python uses a lot of <code>__x__</code> methods (we call them dunder methods).
Above, we see that the constructor is called <code>__init__</code>. Additionally, all
methods take an explicit <code>self</code> parameter, which is similar to <code>this</code> in C++ or
Java.</p>
<p>Python has no visibility controls and instead prefers the convention of
prefixing private/protected properties with an <code>_</code>.</p>
</section>
<section class="slide">
<h3>OOP: <span class="child-header">Usage</span></h3>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> csf = Course('601.229', 'CSF', 'Peter')
>>> csf.number
'601.229'
>>> csf.add_student_grade('Johnny Hopkins', 85)
</code></pre>
</section>
<section class="slide">
<h3>OOP: <span class="child-header">Inheritance</span></h3>
<p>Python supports traditional Java-like inheritance (as well as multiple
inheritance, which we won't delve into).</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">class PassFailCourse(Course):
def add_student_grade(self, student, *, passed):
super().add_student_grade(student, 100 if passed else 0)</code></pre>
<p>We use <code>super()</code> to access methods from ancestor classes. Other methods (in
this example, <code>__init__</code> and <code>curve</code>) are inherited as we would expect.</p>
</section>
<section class="slide">
<h3>OOP: <span class="child-header">Static Methods</span></h3>
<p>In Python methods on a class are called <strong>class methods</strong>, and we denote them
by using a decorator.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">class Course:
# ...
@classmethod
def retrieve_from_db(cls, number):
# Lookup in your database ...
return cls(number, name, instructor)
csf = Course.retrieve_from_db('601.229')</code></pre>
</section>
<section class="slide">
<h3>OOP: <span class="child-header">An Aside on Decorators</span></h3>
<p>Decorators are a powerful Python feature that allow you to augment function
behavior. They are functions that are passed another function and return a new
function with modified behavior.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def cache(func):
func._cache = {}
def wrapped(url):
if url not in func._cache:
func._cache[url] = func(url)
return func._cache[url]
return wrapped</code></pre>
<p>We then can use <code>cache</code> as a decorator:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">@cache
def slow_web_request(url):
print('requesting page')
# ...
return f'page: {url}'</code></pre>
<p>This is equivalent to:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">slow_web_request = cache(slow_web_request)</code></pre>
</section>
<section class="slide">
<h3>OOP: <span class="child-header">Decorators Demo</span></h3>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> slow_web_request('http://www.google.com')
requesting page
'page: http://www.google.com'
>>> slow_web_request('http://www.apple.com')
requesting page
'page: http://www.apple.com'
>>> slow_web_request('http://www.apple.com')
'page: http://www.apple.com'
</code></pre>
</section>
<section class="slide">
<h3>OOP: <span class="child-header">An Aside on Decorators (The Full Story)</span></h3>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">from functools import wraps
def cache(func):
func._cache = {}
@wraps(func)
def wrapped(*args, **kwargs):
frozen_args = (args, tuple(sorted(kwargs.items())))
if frozen_args not in func._cache:
func._cache[frozen_args] = func(*args, **kwargs)
return func._cache[frozen_args]
return wrapped</code></pre>
</section>
<section class="slide">
<h3>OOP: <span class="child-header">More Dunder Methods</span></h3>
<p>There are
<a href="https://docs.python.org/3/reference/datamodel.html#special-method-names">a ton of dunder methods</a>
you can define on your classes.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">class Person:
def __init__(self, first_name, last_name, age):
self.first_name = first_name
self.last_name = last_name
self.age = age
def __hash__(self):
return self.first_name, self.last_name
def __eq__(self, other):
if not isinstance(other, Person):
return False
return self.first_name == other.first_name \
and self.last_name == other.last_name \
and self.age == other.age
def __lt__(self, other):
return self.age < other.age
def __str__(self):
return f'{self.last_name}, {self.first_name} is {self.age} years old'</code></pre>
</section>
<section class="slide">
<h3>OOP: <span class="child-header">Cutting out the Boilerplate</span></h3>
<p>Defining all of these dunder methods can be tedious, so Python 3.7 introduced
<strong>dataclasses</strong>, which give a much more terse way to express classes.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">from dataclasses import dataclass, field
@dataclass
class Person:
first_name: str = field(compare=False)
last_name: str = field(compare=False)
age: int = field(hash=False)</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2><code>Enum</code>s</h2>
<p>Enums allow you to replace magic constants with efficient, named placeholders.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">from enum import Enum, auto
class Color(Enum):
RED = auto()
BLUE = auto()
PURPLE = auto()</code></pre>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> Color.RED
<Color.RED: 1>
>>> Color.RED.name
'RED'
>>> Color.RED == Color.BLUE
False
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.PURPLE: 3>]
</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2>Exceptions</h2>
<p>Python prefers a "do first, ask for forgiveness later" approach to exceptional
cases at runtime. This has the benefit of being safe from data races.</p>
<p><code>IndexError</code> and <code>KeyError</code> are common when indexing into a sequence or
accessing a key in a <code>dict</code>-like data structure.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> cities = ['Boston', 'New York City', 'Los Angeles']
>>> cities[4]
exception: list index out of range
>>> capitals = {'Maryland': 'Annapolis', 'Connecticut': 'Hartford'}
>>> capitals['Hawaii']
exception: 'Hawaii'
</code></pre>
</section>
<section class="slide">
<h3>Exceptions: <span class="child-header">Handling Exceptions</span></h3>
<p>We handle exceptions in Python using a <code>try</code>/<code>except</code> (and maybe an
<code>else</code>/<code>finally</code>).</p>
<p><code>ValueError</code> is commonly used when an argument is in an unexpected format.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">def get_birth_year():
while True:
try:
birth_year = int(input('Enter birth year: '))
except ValueError:
pass
else:
if 1900 <= birth_year <= 2018:
return birth_year</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2>More Data Structures</h2>
<p>The Python standard library has a plethora of useful data structures.</p>
<p>Many common tasks in Python can be written as one-liners by combining these
data structures in clever ways.</p>
</section>
<section class="slide">
<h3>More Data Structures: <span class="child-header"><code>set</code>s</span></h3>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> unique_nums = set([5, 8, 4, 5, 8])
>>> unique_nums
{8, 4, 5}
>>> unique_nums.add(6)
>>> 6 in unique_nums
True
>>> len(unique_nums)
4
>>> unique_nums - set([5, 4])
{8, 6}
>>> unique_nums | set([8, 4])
{4, 5, 6, 8}
</code></pre>
<p>Sets (like most other data structures) can even be created by providing an
iterable (like a generator):</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> set(x * 2 for x in range(8))
{0, 2, 4, 6, 8, 10, 12, 14}
</code></pre>
</section>
<section class="slide">
<h3>More Data Structures: <span class="child-header"><code>defaultdict</code></span></h3>
<p>Behaves like a <code>dict</code>, but returns a default value when a key is not found.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">from collections import defaultdict
names = ['Michael', 'Dwight', 'Jim', 'Pam', 'Meredith']
names_by_first_letter = defaultdict(list)
for name in names:
names_by_first_letter[name[0]].append(name)
print(names_by_first_letter)</code></pre>
<pre><div class="pre-name oneline"><span>output</span></div><code>defaultdict(<class 'list'>, {'M': ['Michael', 'Meredith'], 'D': ['Dwight'], 'J': ['Jim'], 'P': ['Pam']})
</code></pre>
<p>Without it, we'd have to write something like:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">names_by_first_letter = {}
for name in names:
try:
letter_names = names_by_first_letter[name[0]]
except KeyError:
letter_names = names_by_first_letter[name[0]] = []
finally:
letter_names.append(name)</code></pre>
</section>
<section class="slide">
<h3>More Data Structures: <code>defaultdict</code>: <span class="child-header">Another <code>defaultdict</code> example</span></h3>
<p>When implementing Dijkstra's algorithm, a <code>defaultdict</code> is a convenient way to
store the distances from the source node.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">import sys
distance_from_source = defaultdict(lambda: sys.maxsize)
distance_from_source[source] = 0</code></pre>
</section>
<section class="slide">
<h3>More Data Structures: <code>defaultdict</code>: <span class="child-header">When to not use <code>defaultdict</code></span></h3>
<p>If we just want to return a default value (and not also store that default
value), we can use a regular <code>dict</code>:</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> nicknames = {'Robert': 'Bob', 'Kate': 'Katherine'}
>>> nicknames.get('Brenda', 'Brenda')
'Brenda'
>>> nicknames
{'Robert': 'Bob', 'Kate': 'Katherine'}
</code></pre>
</section>
<section class="slide">
<h3>More Data Structures: <span class="child-header"><code>Counter</code></span></h3>
<p>To keep track of counts of just about anything (including other iterables) use
<code>Counter</code>.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> from collections import Counter
>>> spellings = Counter(['color', 'colour', 'color', 'color', 'colour'])
>>> spellings.most_common()
[('color', 3), ('colour', 2)]
>>> spellings.update(['color', 'color'])
>>> spellings.most_common(1)
[('color', 5)]
</code></pre>
</section>
<section class="slide">
<h3>More Data Structures: <span class="child-header"><code>deque</code></span></h3>
<p><code>list</code> has constant-time append and pop (from the end). If you need constant
time operations at both ends (a queue, instead of a stack), use a <code>deque</code>.</p>
<pre><div class="pre-name oneline"><span>Python REPL</span></div><code class="python">>>> from collections import deque
>>> checkout = deque(['oranges', 'blueberries'])
>>> checkout.append('strawberries')
>>> checkout
deque(['oranges', 'blueberries', 'strawberries'])
>>> checkout.appendleft('eggs')
>>> checkout
deque(['eggs', 'oranges', 'blueberries', 'strawberries'])
>>> checkout.popleft()
'eggs'
</code></pre>
<!---------------------------------------------------------------------------->
</section>
<section class="slide">
<h2>File I/O</h2>
<p>File handling (and resource management) in Python is a breeze thanks to
<strong>context managers</strong>. These provide automatic cleanup of resources by using a
<code>with</code> statement.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python"># By default, files are opened for reading
with open('essay.txt') as f:
essay = f.read()</code></pre>
<p>This is almost equivalent to the following, but with one important distinction:
<strong>exception handling!</strong></p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">f = open('essay.txt')
essay = f.read()
# What if an exception happens here?
f.close()</code></pre>
</section>
<section class="slide">
<h3>File I/O: <span class="child-header">Examples</span></h3>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python"># By default, files are opened for reading
with open('essay.txt') as f:
essay = f.read()
translated = translate(essay, from_language='english',
to_language='french')
# 'w' truncates the file, then opens it for writing
with open('essay_translated.txt', 'w') as f:
print(translated, file=f)</code></pre>
<p>Reading a file line-by-line is also straightforward.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python"># Each line in urls.txt contains a url
with open('urls.txt') as f:
for url in f:
download_file(url)</code></pre>
</section>
<section class="slide">
<h3>File I/O: <span class="child-header">CSV</span></h3>
<p>Python makes reading CSVs super simple.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">import csv
with open('games.csv') as f:
for home_team, away_team, score in csv.reader(f):
print(f'{away_team} @ {home_team}: {score}')</code></pre>
</section>
<section class="slide">
<h3>File I/O: <span class="child-header">Gzip</span></h3>
<p>And handling compressed data is just requires a slight modification.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">import csv
import gzip
with gzip.open('games.csv.gz') as f:
for home_team, away_team, score in csv.reader(f):
print(f'{away_team} @ {home_team}: {score}')</code></pre>
</section>
<section class="slide">
<h3>File I/O: <span class="child-header">Pickle Serialization</span></h3>
<p>Python provides a serialization method that works out of the box for every
standard library class and almost every class you'll create:</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">import pickle
with open('dump.pkl', 'wb') as f:
pickle.dump(f, my_object)
# Then later...
with open('dump.pkl', 'rb') as f:
my_object = pickle.load(f)</code></pre>
</section>
<section class="slide">
<h3>File I/O: <span class="child-header">JSON Serialization</span></h3>
<p>JSON serialization can be done in an almost identical way.</p>
<pre><div class="pre-name oneline"><span>python</span></div><code class="python">import json
with open('dump.json', 'w') as f:
json.dump(f, my_dict)
# Then later...