-
Notifications
You must be signed in to change notification settings - Fork 1
/
effective2.html
1238 lines (1102 loc) · 45 KB
/
effective2.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>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<title>Effective Python一人輪読会(Item 19 to 36) — Daydreaming in Greater Boston</title>
<meta name="author" content="Kyos">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/favicon.png" rel="icon">
<link href="/theme/css/main.css" media="screen, projection"
rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic"
rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic"
rel="stylesheet" type="text/css">
</head>
<body>
<header role="banner"><hgroup>
<h1><a href="/">Daydreaming in Greater Boston</a></h1>
</hgroup></header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
</ul>
<ul class="main-navigation">
<li><a href="/pages/about.html">About Me</a></li>
<li >
<a href="/category/blog.html">Blog</a>
</li>
<li >
<a href="/category/english.html">English</a>
</li>
<li >
<a href="/category/linux.html">Linux</a>
</li>
<li class="active">
<a href="/category/python.html">Python</a>
</li>
<li >
<a href="/category/tech.html">Tech</a>
</li>
</ul></nav>
<div id="main">
<div id="content">
<div>
<article class="hentry" role="article">
<header>
<h1 class="entry-title">Effective Python一人輪読会(Item 19 to 36)</h1>
<p class="meta">
<time datetime="2020-08-18T00:00:00-04:00" pubdate>Tue 18 August 2020</time> </p>
</header>
<div class="entry-content"><div id="table-of-contents">
<h2>Table of Contents</h2>
<div id="text-table-of-contents">
<ul>
<li><a href="#org70826ba">1. Chapter 3: 関数</a>
<ul>
<li><a href="#org3cb5ba8">1.1. Item 19: 関数が複数の値を返すとき、4つ以上の変数にunpackするな</a></li>
<li><a href="#orgad7483f">1.2. Item 20: Noneを返すくらいなら例外を上げよ</a></li>
<li><a href="#org3964a05">1.3. Item 21: Closuresが変数のスコープにどう影響するかを知っておけ</a></li>
<li><a href="#org1145a1c">1.4. Item 22: 可変数のpositional argumentsを使って見やすくしろ</a></li>
<li><a href="#org5d69fbd">1.5. Item 23: オプションとなる挙動はキーワード引数で与えよ</a></li>
<li><a href="#org306cda8">1.6. Item 24: 動的なデフォルト引数を指定するときはNoneとDocstringsを使え</a></li>
<li><a href="#org130acb9">1.7. Item 25: キーワードオンリー引数、位置オンリー引数を使って、明確さを強制せよ</a></li>
<li><a href="#org2c05f0e">1.8. Item 26: 関数のデコレーターをfunctools.wrapsを使って定義せよ</a></li>
</ul>
</li>
<li><a href="#org59d634c">2. Chapter 4: Comprehensionsとジェネレーター</a>
<ul>
<li><a href="#org8c0a965">2.1. Item 27: mapとfilterの代わりにcomprehensionsを使え</a></li>
<li><a href="#org9c68236">2.2. Item 28: 3つ以上のコントロールsubexpressionsをcomprehensionsで使うな</a></li>
<li><a href="#org1405afa">2.3. Item 29: Assignment表現を使って、comprehensions内での繰り返しを避けよ</a></li>
<li><a href="#org771389a">2.4. Item 30: リストを返すくらいならジェネレーターを考慮せよ</a></li>
<li><a href="#orge15a8d3">2.5. Item 31: 引数をたどるときには保守的になれ</a></li>
<li><a href="#orgb382ee5">2.6. Item 32: 大きなリストcomprehensionsの代わりにジェネレーターexpressionsを考えよ</a></li>
<li><a href="#orgea3dcce">2.7. Item 33: yield fromを使って複数のジェネレーターを組み合わせよ</a></li>
<li><a href="#org0900ece">2.8. Item 34: ジェネレーターにsendを使ってデータを送ってはいけない</a></li>
<li><a href="#org3cbec98">2.9. Item 35: throwで例外を投げてジェネレータの状態遷移を起こすな</a></li>
<li><a href="#org93390ad">2.10. Item 36: Iteratorやジェネレーターを使うときにはitertoolsの使用を検討せよ</a>
<ul>
<li><a href="#orgf8f7d75">2.10.1. Iteratorsをつなぐ</a></li>
<li><a href="#org4d3883c">2.10.2. Iteratorからアイテムをフィルタする</a></li>
<li><a href="#org3b2bdcc">2.10.3. 複数iteratorsのアイテムを結びつける</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-org70826ba" class="outline-2">
<h2 id="org70826ba"><span class="section-number-2">1</span> Chapter 3: 関数</h2>
<div class="outline-text-2" id="text-1">
</div>
<div id="outline-container-org3cb5ba8" class="outline-3">
<h3 id="org3cb5ba8"><span class="section-number-3">1.1</span> Item 19: 関数が複数の値を返すとき、4つ以上の変数にunpackするな</h3>
<div class="outline-text-3" id="text-1-1">
<p>
うっかり順番を間違えたりするので。catch-allの星付きexpression(eg, *others)を使うか、namedtupleを使う。
</p>
<p>
namedtupleについては<a href="https://dbader.org/blog/writing-clean-python-with-namedtuples">ここ</a>の説明がわかりやすかったです。(逆にIntroducing Pythonの説明はさっぱり。。。) 以下のように使います。
</p>
<div class="org-src-container">
<pre class="src src-python">from collections import namedtuple
Car = namedtuple('Car', 'color milage')
または
Car = namedtuple('Car', ['color', 'milage'])
</pre>
</div>
<p>
前者の文字列部分(<code>'color milage'</code>)は内部で <code>split()</code> されてリストになるため、後者と等価だそうです。
</p>
<p>
メソッドの無いimmutableなクラスのように使えます。
</p>
<div class="org-src-container">
<pre class="src src-python">my_car = Car('red', 3812.4)
</pre>
</div>
<p>
namedtupleは内部ではクラスとして表現されていますが、メモリ効率は良いそうです。
</p>
<p>
複数の値を返す関数は、それらをタプルにして返すことは知りませんでした。関数の呼び出し元が複数の変数にunpackする時にタプルから取り出します。
</p>
<p>
もう一つ。日本語ではあまり馴染みがありませんが、英語ではよく使うmaiden(中央値)。要素が偶数の場合にどうするのか知りませんでした。真ん中の二つの平均を取るのですか。
</p>
<div class="org-src-container">
<pre class="src src-python">if count % 2 == 0:
lower = sorted_numbers[middle - 1]
upper = sorted_numbers[middle]
median = (lower + upper) / 2
</pre>
</div>
</div>
</div>
<div id="outline-container-orgad7483f" class="outline-3">
<h3 id="orgad7483f"><span class="section-number-3">1.2</span> Item 20: Noneを返すくらいなら例外を上げよ</h3>
<div class="outline-text-3" id="text-1-2">
<p>
<code>None</code> や0, 空の文字列, etc.は全て <code>False</code> と解釈されるので、if文などでの判定でうっかり間違いやすい。
</p>
<p>
適切な例外(そのままでなく)を上げて、それをドキュメントに書く。更にtype annotationsする。
</p>
<pre class="example">
def careful_divide(a: float, b: float) -> float:
"""Divides a by b.
Raises:
ValueError: When the inputs cannot be divided.
""""
try:
return a / b
except ZeroDivisionError as e:
raise ValueError('Invalid inputs')
</pre>
</div>
</div>
<div id="outline-container-org3964a05" class="outline-3">
<h3 id="org3964a05"><span class="section-number-3">1.3</span> Item 21: Closuresが変数のスコープにどう影響するかを知っておけ</h3>
<div class="outline-text-3" id="text-1-3">
<p>
えーっと、closuresって何でしたっけ? Introducing Pythonを読んだときに消化不良のまま終わったような気が。。
</p>
<p>
30分ほど調べてみました。<a href="https://www.programiz.com/python-programming/closure">ここ</a>の定義が一番しっくり来ました。
</p>
<ul class="org-ul">
<li>関数と、そのインナー関数がある</li>
<li>インナー関数は、外側の関数で定義された変数を参照する</li>
<li>外側の関数は、インナー関数を戻り値として返す</li>
</ul>
<div class="org-src-container">
<pre class="src src-python">def print_msg(msg):
def printer(): # inner function
print(msg) # msg was defined in the enclosing function
return printer # return the inner function
</pre>
</div>
<p>
これを使うには、
</p>
<div class="org-src-container">
<pre class="src src-python">another = print_msg("Hello")
another()
>>>
Hello
</pre>
</div>
<p>
メリットの説明は<a href="http://www.trytoprogram.com/python-programming/python-closures/">ここ</a>がわかりやすかったです。
</p>
<ul class="org-ul">
<li>コールバックとして定義されるため、ある意味データ隠蔽(hiding)に使える</li>
<li>ハードコードされた定数や文字列の代わりに使える</li>
<li>コードに関数が1, 2個しかないときに有効</li>
</ul>
<p>
この節では、やたらと小難しい具体例が続きますが、言っていることは単純です: インナー関数でアサインしている変数のスコープは、インナー関数で閉じる(ie, 外側の関数に届かない)。そして、もし外側の関数にスコープを広げたい場合は <code>nonlocal</code> を付けます。
</p>
<p>
この <code>nonlocal</code> の使い方にも注意が必要で、長い関数だと <code>nonlocal</code> であることがわかりづらくなります。単純な関数以外ではヘルパークラスにするのがよい、とのこと。
</p>
<div class="org-src-container">
<pre class="src src-python">class SomeClass:
def __call__(self, x, y):
<snip>
sc = SomeClass()
sc(5, 7) # __call__が使われる
</pre>
</div>
<p>
<code>__call__()</code> スペシャル関数は、クラスを関数であるかのように振る舞わせる関数。なお、この大事なところでKindle版は最後の <code>return (1, x)</code> のインデントがずれています。しかも、Click here to view code imageがここには何故か無い。どうしろと。。。
</p>
</div>
</div>
<div id="outline-container-org1145a1c" class="outline-3">
<h3 id="org1145a1c"><span class="section-number-3">1.4</span> Item 22: 可変数のpositional argumentsを使って見やすくしろ</h3>
<div class="outline-text-3" id="text-1-4">
<ul class="org-ul">
<li>可変長の引数(0個含む)は関数の定義def文において <code>*args</code> のようにアスタリスク付きで書くことで実現できる</li>
<li>関数の呼び出し側で、 <code>*vars</code> としてアスタリスクを付けて引数を指定することで、リスト等のシーケンスの中身を(展開して)渡せる</li>
</ul>
<p>
二つ目において、呼び出し側がgeneratorにアスタリスクを付けると、呼び出しのたびにgeneratorを全て展開してしまうため、メモリ圧迫&クラッシュに注意、とのことです。
</p>
</div>
</div>
<div id="outline-container-org5d69fbd" class="outline-3">
<h3 id="org5d69fbd"><span class="section-number-3">1.5</span> Item 23: オプションとなる挙動はキーワード引数で与えよ</h3>
<div class="outline-text-3" id="text-1-5">
<p>
ここは特にコメントなしです。
</p>
</div>
</div>
<div id="outline-container-org306cda8" class="outline-3">
<h3 id="org306cda8"><span class="section-number-3">1.6</span> Item 24: 動的なデフォルト引数を指定するときはNoneとDocstringsを使え</h3>
<div class="outline-text-3" id="text-1-6">
<p>
例:
</p>
<div class="org-src-container">
<pre class="src src-python">def log(message, when=None):
“““Log a message with a timestamp.
Args:
message: Message to print.
when: datetime of when the message occurred.
Defaults to the present time.
”””
if when is None:
when = datetime.now()
print(f'{when}: {message}')
</pre>
</div>
<p>
上の関数において、whenのデフォルトはNoneとしておき、関数の中で現在時刻を設定しています。def文で <code>when=datetime.now()</code> を書いても機能しないのは、 <code>datetime.now()</code> が評価されるのが関数のロード時のみだからです。同様に、 <code>{}</code> , <code>[]</code> のような動的な値を使うときにも <code>None</code> をデフォルト値として置きます。
</p>
<p>
例えば、以下のケースでは <code>{}</code> の同一オブジェクトが各呼び出し間で共有されてしまいます。
</p>
<div class="org-src-container">
<pre class="src src-python">def decode(data, dafault={})
try:
return json.loads(data)
except ValueError:
return default
</pre>
</div>
</div>
</div>
<div id="outline-container-org130acb9" class="outline-3">
<h3 id="org130acb9"><span class="section-number-3">1.7</span> Item 25: キーワードオンリー引数、位置オンリー引数を使って、明確さを強制せよ</h3>
<div class="outline-text-3" id="text-1-7">
<p>
<code>float('inf')</code> は無限大を意味するそうです。
</p>
<p>
引数リストにあるアスタリスク <code>*</code> は、位置引数の終わりと、キーワードオンリー引数の始まりを強制します。また、Python 3.8より、引数リストにある <code>/</code> は、これよりも前の引数は位置オンリー引数であることを強制します。例えば、
</p>
<pre class="example">
def func(num, div, /, *, ov_flag=False, zd_flag=True):
</pre>
<p>
とあったときに、
</p>
<pre class="example">
func(10, 2, True, False)
</pre>
<p>
や
</p>
<pre class="example">
func(num=10, div=2)
</pre>
<p>
などと呼び出すとTypeErrorとなります。
</p>
<p>
<code>/</code> と <code>*</code> に挟まれた引数は、位置引数としてもキーワード引数としてもどちらでもよいそうです。これがPythonのデフォルトです。
</p>
</div>
</div>
<div id="outline-container-org2c05f0e" class="outline-3">
<h3 id="org2c05f0e"><span class="section-number-3">1.8</span> Item 26: 関数のデコレーターをfunctools.wrapsを使って定義せよ</h3>
<div class="outline-text-3" id="text-1-8">
<p>
コードスニップを見ていると、見慣れない表現が。。。
</p>
<pre class="example">
{args!r}
</pre>
<p>
この <code>!r</code> は、 <code>__repr__()</code> で解釈せよ、という意味だそうです。 f'This string {name!r} is a good one' のように使います。関係ありませんが、 <code>!r</code> はgoogle等で検索しづらいですね。。
</p>
<p>
デコレーターは難しいですね。 <a href="https://lerner.co.il/2019/05/05/making-your-python-decorators-even-better-with-functool-wraps/">ここ</a> の説明がわかりやすかったです。なるほど、 <code>__name__</code> や <code>__doc__</code> を表示したり <code>help()</code> を使うと、インナー関数でなくwrapper関数自体の情報が表示されてしまうようです。
</p>
<p>
その解決策がfunctools.wrapsを使うことで、
</p>
<div class="org-src-container">
<pre class="src src-python">from functools import wraps
def mydeco(func):
@wraps(func)
def wrapper(*args, *kwargs):
return f'{func(args, **kwargs)}!!!'
return wrapper
</pre>
</div>
<p>
<code>wraps</code> デコレーターはインナー関数のdoc stringやその他の情報を外側のwrapper関数にコピーしてくれます。
</p>
</div>
</div>
</div>
<div id="outline-container-org59d634c" class="outline-2">
<h2 id="org59d634c"><span class="section-number-2">2</span> Chapter 4: Comprehensionsとジェネレーター</h2>
<div class="outline-text-2" id="text-2">
<p>
<a href="https://jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/#:~:text=A%20Python%20generator%20is%20a,as%20the%20%22generated%22%20value.">ここ</a>を参考にして、ジェネレーターについて整理します。(このリンクはかなり詳細に説明しています)
</p>
<ul class="org-ul">
<li>ジェネレーター関数は、 <code>return</code> の代わりに <code>yield</code> を持つ関数で、ジェネレーター(=ジェネレーターiterator)を作る。</li>
<li>ジェネレーターは特定のタイプのiterator。iteratorとして機能するために、ジェネレーターは <code>__next__()</code> メソッドを持つ。</li>
<li>ジェネレーターから次の値を得るには、iterators: <code>next()</code> を使う。( <code>next()</code> は <code>__next__()</code> メソッドを呼ぶ)</li>
<li>ジェネレーターは <code>next()</code> の呼び主に対して、 <code>yield</code> 文で値を返す。</li>
</ul>
<p>
ジェネレーターの使い方
</p>
<ul class="org-ul">
<li>一通りたどるとジェネレーターは <code>StopIteration</code> 例外を上げる。これは普通のiteratorと同じ挙動で、for文などでは問題視せずにサイレントに抜ける。</li>
<li>その後、もう一度ジェネレーターを呼ぶと <code>StopIteration</code> 例外を上げる。</li>
<li>ジェネレーター関数をもう一度呼んで、新たなジェネレーターを作り直すことができる。</li>
</ul>
</div>
<div id="outline-container-org8c0a965" class="outline-3">
<h3 id="org8c0a965"><span class="section-number-3">2.1</span> Item 27: mapとfilterの代わりにcomprehensionsを使え</h3>
<div class="outline-text-3" id="text-2-1">
<p>
<code>map()</code> ビルトイン関数は知りませんでした。<a href="https://www.geeksforgeeks.org/python-map-function/">ここ</a>によると、第二引数のiterable(例: リスト)の各アイテムに、第一引数の関数を適用して、iterableなmapオブジェクトを返す、とのこと。
</p>
<pre class="example">
mapobj = map(lambda x: x ** 2, somelist)
</pre>
<p>
<code>filter</code> ビルトイン関数も知りませんでしたが、名前から明らかです。
</p>
<pre class="example">
filter(lambda x: x % 2 == 0, somelist)
</pre>
<p>
<code>filter</code> もフィルターされたiteratorを返します。
</p>
<p>
両方とも返すのはiteratorですが、リストcomprehensionsと似てますね。
</p>
<p>
<code>map</code> と <code>filter</code> を組み合わせると読みづらいので、スッキリとかけるcomprehensionsを使え、ということでした。 <code>somelist</code> の中から2で割りきれる要素のみ2乗したいとき、
</p>
<pre class="example">
map(lambda x: x**2, filter(lambda x: x % 2 == 0, somelist))
</pre>
<p>
これが、
</p>
<pre class="example">
[x**2 for x in somelist if x % 2 == 0]
</pre>
<p>
。。。確かに。
</p>
</div>
</div>
<div id="outline-container-org9c68236" class="outline-3">
<h3 id="org9c68236"><span class="section-number-3">2.2</span> Item 28: 3つ以上のコントロールsubexpressionsをcomprehensionsで使うな</h3>
<div class="outline-text-3" id="text-2-2">
<p>
初級者的にキツい表現が出ました。やりたいことは、二次元にネストされたリストであるmatrixの中身をflattenする(一次元にする)、です。えーと、ネストされた <code>for</code> は左から解釈するらしいので、、
</p>
<pre class="example">
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
</pre>
<p>
最初の <code>for</code> で <code>matrix</code> の各アイテム= <code>row</code> を取り出して、次の <code>for</code> で <code>row</code> の中の <code>x</code> を取り出し、これを使ってリストを作る、ということでした。
</p>
<pre class="example">
print(flat)
>>>
[1, 2, 3, 4, 5, 6, 7, 8, 9]
</pre>
<p>
これは便利そうです。
</p>
<p>
次は、先ほど <code>matrix</code> の要素を二乗する(flattenしない)場合。
</p>
<pre class="example">
squared = [[x ** 2 for x in row] for row in matrix]
print(squared)
>>>
[[1, 4, 9], [16, 25, 36], [49, 64, 81]]
</pre>
<p>
このリストcomprehensionのリストが二重になっているので、flattenしないのですね。
</p>
<p>
お題のコントロールsubexpressionsは、 <code>for</code> によるループだけでなく、 <code>for</code> に付けられる <code>if</code> も指していました。下の例は <code>for</code> が二つと <code>if</code> があるのでアウト。
</p>
<pre class="example">
[x for wor in matrix for x in row if x % 2 == 0]
</pre>
</div>
</div>
<div id="outline-container-org1405afa" class="outline-3">
<h3 id="org1405afa"><span class="section-number-3">2.3</span> Item 29: Assignment表現を使って、comprehensions内での繰り返しを避けよ</h3>
<div class="outline-text-3" id="text-2-3">
<p>
Assignment expressions - Walrusのやつです(<code>:=</code>)。これは便利なので、是非とも使っていきたいと思います。
</p>
<p>
次のようなストックがあって、
</p>
<div class="org-src-container">
<pre class="src src-python">stock = {
'nails': 125,
'screws': 35,
'wingnuts': 8,
'washers': 24,
}
</pre>
</div>
<p>
次のようなオーダー(8個単位とする)が来た場合について考えます。
</p>
<pre class="example">
order = ['screws', 'wingnuts', 'clips']
</pre>
<p>
ディクショナリcomprehensionを使って、次のように書くと良いです。
</p>
<div class="org-src-container">
<pre class="src src-python">def get_batches(count, size):
return count // size
found = {name: batches for name in order
if (batches := get_batches(stock.get(name, 0), 8))}
</pre>
</div>
<p>
<code>get_batches</code> を使って、8個のセットが何セットあるかを求めています。
ポイントWalrusを使って <code>get_batches</code> の実行をif節の中で1回だけ使っているところ。
</p>
<pre class="example">
print(found)
>>>
{'screws': 4, 'wingnuts': 1}
</pre>
<p>
知りませんでした、generator expressions。<a href="https://dbader.org/blog/python-generator-expressions">ここ</a>の説明がわかりやすかったです。まずは似たようなリストcomprehension:
</p>
<pre class="example">
listcomp = ['Hello' for i in range(3)]
</pre>
<p>
これがジェネレーターexpression:
</p>
<pre class="example">
genexpr = ('Hello' for i in range(3))
</pre>
<p>
後者が返すのはジェネレーターです。
</p>
<div class="org-src-container">
<pre class="src src-python">>>> next(genexpr)
'Hello'
>>> next(genexpr)
'Hello'
>>> next(genexpr)
'Hello'
>>> next(genexpr)
StopIteration
</pre>
</div>
<p>
先ほどの例はジェネレーターexpressionを使っても書けます。
</p>
<pre class="example">
found = ((name, batches) for name in order
if (batches := get_batches(stock.get(name, 0), 8)))
</pre>
<p>
もちろん、返るのはジェネレーター。
</p>
<div class="org-src-container">
<pre class="src src-python">print(next(found))
print(next(found))
>>>
('screws', 4)
('wingnuts', 1)
</pre>
</div>
</div>
</div>
<div id="outline-container-org771389a" class="outline-3">
<h3 id="org771389a"><span class="section-number-3">2.4</span> Item 30: リストを返すくらいならジェネレーターを考慮せよ</h3>
<div class="outline-text-3" id="text-2-4">
<p>
お、なんだか今更ジェネレーターの説明がありました。どうしてジェネレーターexpressionsを説明なしで使った後にこれが来るかな。。。
</p>
<p>
この本は、どこから読んでも良いと謳っていますが、逆に、最初から読もうが途中から読もうが読みづらさは変わらない、という残念なことになっていると思います。
</p>
<ul class="org-ul">
<li>リストにまとめて返すより、generatorsを使ったほうがよりクリア</li>
<li>generatorsはメモリ使用量が少ない</li>
</ul>
</div>
</div>
<div id="outline-container-orge15a8d3" class="outline-3">
<h3 id="orge15a8d3"><span class="section-number-3">2.5</span> Item 31: 引数をたどるときには保守的になれ</h3>
<div class="outline-text-3" id="text-2-5">
<p>
ジェネレーター関数の <code>read_visits()</code> は、1行に一つの数字(入場者数)が書いてあるファイルを読んで yield します。
</p>
<pre class="example">
def read_visits(data_path):
with open(data_path) as f:
for line in f:
yield int(line)
</pre>
<p>
<code>normalize_func()</code> は入場者数をyieldするiterator(正確にはiteratorを返すジェネレーター関数)を引数に取り、毎日の入場者数をパーセントに変換したリストを返します。
</p>
<div class="org-src-container">
<pre class="src src-python">def normalize_func(get_iter):
total = sum(get_iter()) # 最初にiteratorを使いきる
result = []
for value in get_iter(): # 更にiteratorをループで回す
percent = 100 * value / total
result.append(percent)
return result
</pre>
</div>
<p>
この時、以下がどうなるかぱっと見、わかりずらいです。特にlambdaが何をしているのか。
</p>
<pre class="example">
percentages = normalize_func(lambda: read_visits(path))
</pre>
<p>
もしlambdaが無ければ、normalize_funcの引数read_visits(path)はiteratorをそのまま返します。normalize_funcに渡したiteratorは最初にsumを取るところでexhaustされ、次のfor文では既にexhaustしたままのため、このfor文のブロックは一度も実行されません。
</p>
<p>
これに <code>lambda</code> が付くと、normalize_func()に渡されるlambdaの無名関数は、iteratorそのものでなく、iteratorを(呼び出しの度に)返す関数です。
</p>
<p>
sum関数でiteratorをいったんexhaustさせますが、次のfor文ではまた新たな(ie, exhaustしていない)iteratorが返されるので、for文ブロックが期待通り実行されます。
</p>
<p>
ただ、お勧めのやり方はこれではなく、自分で <code>__iter__()</code> メソッドを書いたクラスを用意することでした。 <code>__iter__()</code> メソッドはジェネレーター関数でなくてはならず、iteratorを返します。
</p>
<div class="org-src-container">
<pre class="src src-python">class ReadVisits:
def __init__(self, data_path):
self.data_path = data_path
def __iter__(self):
with open(self.data_path) as f:
for line in f:
yeild int(line)
</pre>
</div>
<p>
iteratorを返す <code>__iter__()</code> メソッドを持つクラスを用意することで、このクラスのオブジェクトをfor文などでiterateすることが可能です。
</p>
<p>
また、normalize_funcにおいて、引数がiteratorそのものならばexceptionを上げるようにチェックを追加できます。
</p>
<div class="org-src-container">
<pre class="src src-python">def normalize_defensive(numbers):
if iter(numbers) is numbers: # An iterator -- bad!
</pre>
</div>
<p>
または、
</p>
<div class="org-src-container">
<pre class="src src-python">from collections.abc import Iterator
def normalize_defensive(numbers):
if ininstance(numbers, Iterator):
</pre>
</div>
</div>
</div>
<div id="outline-container-orgb382ee5" class="outline-3">
<h3 id="orgb382ee5"><span class="section-number-3">2.6</span> Item 32: 大きなリストcomprehensionsの代わりにジェネレーターexpressionsを考えよ</h3>
<div class="outline-text-3" id="text-2-6">
<p>
(今更ですが)ジェネレーターexpressionsの説明でした。その戻り値であるiteratorを入力とした、ジェネレーターexpressionsのチェイン実行も可能で効率的。
</p>
<pre class="example">
it = (len(x) for x in open('my_file.txt'))
roots = ((x, x**0.5) for x in it)
</pre>
<p>
listcompsは大量のインプットがある時にメモリを使いすぎてしまいますが、genexpsはメモリ効率がとても良いです。
</p>
</div>
</div>
<div id="outline-container-orgea3dcce" class="outline-3">
<h3 id="orgea3dcce"><span class="section-number-3">2.7</span> Item 33: yield fromを使って複数のジェネレーターを組み合わせよ</h3>
<div class="outline-text-3" id="text-2-7">
<p>
<code>yield from</code> です。for文で <code>yield</code> を回すところを <code>yeild from</code> とシンプルに書け、リーダビリティーを向上させるだけでなく、ループで <code>yield</code> を回すよりも高速です。 <code>yield</code> を組み合わせるケースでは、可能な限り <code>yeild from</code> を使うべきです。
</p>
<div class="org-src-container">
<pre class="src src-python">def slow():
for i in child():
yield i
</pre>
</div>
<p>
は、以下のようによりシンプルに書けます。
</p>
<div class="org-src-container">
<pre class="src src-python">def fast():
yield from child()
</pre>
</div>
</div>
</div>
<div id="outline-container-org0900ece" class="outline-3">
<h3 id="org0900ece"><span class="section-number-3">2.8</span> Item 34: ジェネレーターにsendを使ってデータを送ってはいけない</h3>
<div class="outline-text-3" id="text-2-8">
<p>
えー、前の項目で何とか理解したばかりだというのに、 <code>send()</code> を使うなと言っています。初心者にわかりづらいのと、 <code>yield from</code> を使って初回に <code>None</code> が返るケースでびっくりすることがあるため。この節はかなり難しいです。
</p>
<div class="org-src-container">
<pre class="src src-python">def my_generator():
received = yield 1
it = iter(my_genarator())
output = it.send(None)
</pre>
</div>
<p>
このコードにおいて、最後の行でitに対して(初めて)sendが呼ばれる時、 <code>yield 1</code> 部分のみが実行され、 <code>received =</code> 部分はまだ実行されません。つまり、sendからは何も受け取りません。receivedにsendから値が渡されるのはit.sendが再度呼ばれてジェネレーターが再開するタイミングです。確かに、慣れていないとわかりずらいです。
</p>
<div class="org-src-container">
<pre class="src src-python">def wave_modulating(steps):
step_size = 2 * math.pi / steps
amplitude = yield # Receive initial amplitude
for step in range(steps):
radians = step * step_size
fraction = math.sin(radians)
output = amplitude * fraction
amplitude = yield output # Receive next amplitude
</pre>
</div>
<p>
上記で、最初にamplitudeの初期値を受け取るところが、yield fromするとNoneが返る箇所です。
</p>
<div class="org-src-container">
<pre class="src src-python">def complex_wave_modulating():
yield from wave_modulating(3)
yield from wave_modulating(4)
yield from wave_modulating(5)
</pre>
</div>
<div class="org-src-container">
<pre class="src src-python">def run_modulating(it):
amplitudes = [
None, 7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10]
for amplitude in amplitudes:
output = it.send(amplitude)
transmit(output) # outputをprintする
</pre>
</div>
<p>
complex_wave_modulatingの <code>yield from</code> と、run_modulatingの <code>send</code> を以下のように組み合わせます。複雑でだいぶ頭が混乱しますが、、、
</p>
<div class="org-src-container">
<pre class="src src-python"> run_modulating(complex_wave_omdulating())
>>>
Output is None
Output: 0.0
Output: 6.1
Output: -6.1
Output is None
Output: 0.0
Output: 2.0
Output: 0.0
Output: -10.0
Output is None
Output: 0.0
Output: 9.5
Output: 5.9
</pre>
</div>
<p>
。。。うまくありません。yield fromで引数を渡すところがNoneを返すため、出力にNoneが入り込んでしまいます。yield fromとsendの組み合わせはいまいちです。
</p>
<p>
これを、 <code>send()</code> を使わずに、iteratorを引数で渡しておくように修正します。
</p>
<div class="org-src-container">
<pre class="src src-python">def wave_cascading(amplitude_it, steps):
step_size = 2 * math.pi / steps
for step in range(steps):
radians = step * step_size
fraction = math.sin(radians)
amplitude = next(amplitude_it) # Get next input
output = amplitude * fraction
yield output
</pre>
</div>
<p>
最初の呼び出しでNoneをyieldすることが無くなりました。
</p>
<p>
そして、次のcomplex_cascadingから、このwave_cascadingをyield fromで組み合わせてジェネレーター関数のカスケードをします。
</p>
<div class="org-src-container">
<pre class="src src-python">def complex_cascading(amplitude_it):
yield from wave_cascading(amplitude_it, 3)
yield from wave_cascading(amplitude_it, 4)
yield from wave_cascading(amplitude_it, 5)
</pre>
</div>
<p>
そして更に、
</p>
<div class="org-src-container">
<pre class="src src-python">def run_cascading():
amplitudes = [7, 7, 7, 2, 2, 2, 2, 10, 10, 10, 10, 10]
it = complex_cascading(iter(amplitudes))
for amplitude in amplitudes:
output = next(it)
transmit(output)
</pre>
</div>
<p>
amplitudesから抽出したiteratorをcomplex_cascadingに渡してiterator <code>it</code> を作ります。そして、amplitudesをループで回し、 <code>it</code> からyieldした値をtransmitします。
</p>
<p>
なんだかやたらと複雑ですが、transmitでNoneが返らずに正しく動くようになりました。
</p>
<p>
まとめると、カスケードさせたジェネレーターとsendを組み合わせるのは筋が悪く、iteratorを引数として渡した方がよい、ということでした。
</p>
</div>
</div>
<div id="outline-container-org3cbec98" class="outline-3">
<h3 id="org3cbec98"><span class="section-number-3">2.9</span> Item 35: throwで例外を投げてジェネレータの状態遷移を起こすな</h3>
<div class="outline-text-3" id="text-2-9">
<p>
<code>throw</code> を使う駄目な例:
</p>
<div class="org-src-container">
<pre class="src src-python">def run():
it = timer(4)
while True:
try:
if check_for_reset():
current = it.throw(Reset()) # 自分で例外を投げている
else:
current = next(it) # ここで例外が発生する可能性あり
except StopIteration:
break
else: # 例外が発生しなかったら
announce(current)
</pre>
</div>
<p>
何だか、何がしたいのかよくわかりません。。。。ので調べます。<a href="https://stackoverflow.com/questions/11485591/what-is-generator-throw-good-for">ここ</a>によると、 <code>it.throw()</code> はit内部で例外を発生させるようです。 <code>it</code> の <code>Reset()</code> 例外ハンドラーでタイマーをリセットしているのかな。確かに、少し読みづらいですかね。
</p>
<p>
ジェネレーターの <code>__iter__()</code> を持つクラスを用意して <code>run()</code> を書き換えます。
</p>
<div class="org-src-container">
<pre class="src src-python">class Timer:
def __init__(self, period):
self.current = period
self.period = period
def reset(self):
self.current = self.period
def __iter__(self):
while self.current:
self.current -= 1
yield self.current
</pre>
</div>
<p>
<code>run()</code> 関数はずっとシンプルになります。
</p>
<div class="org-src-container">
<pre class="src src-python">def run():
timer = Timer(4)
for current in timer:
if check_for_reset():
timer.reset()
announce(current)
</pre>
</div>
</div>
</div>
<div id="outline-container-org93390ad" class="outline-3">
<h3 id="org93390ad"><span class="section-number-3">2.10</span> Item 36: Iteratorやジェネレーターを使うときにはitertoolsの使用を検討せよ</h3>
<div class="outline-text-3" id="text-2-10">
<p>
itertoolsには便利な関数がたくさんあるので使うとよい、という節でした。
</p>
</div>
<div id="outline-container-orgf8f7d75" class="outline-4">
<h4 id="orgf8f7d75"><span class="section-number-4">2.10.1</span> Iteratorsをつなぐ</h4>
<div class="outline-text-4" id="text-2-10-1">
</div>
<ol class="org-ol">
<li><a id="org7c5697e"></a>chain<br />
<div class="outline-text-5" id="text-2-10-1-1">
<div class="org-src-container">
<pre class="src src-python">it = itertools.chain([1, 2, 3], [4, 5, 6])
print(list(it))
>>>
[1, 2, 3, 4, 5, 6]
</pre>
</div>
</div>
</li>
<li><a id="orgeda6bab"></a>repeat<br />
<div class="outline-text-5" id="text-2-10-1-2">
<div class="org-src-container">
<pre class="src src-python">it = itertools.repeat('hello', 3)
print(list(it))
>>>
['hello', 'hello', 'hello']
</pre>
</div>
<p>
もっと簡単な方法はないのでしょうか。実験してみます。
</p>
<pre class="example">
>>> 'hello' * 3
'hellohellohello'
>>> ['hello' * 3]
['hellohellohello']
>>> list('hello' * 3)
['h', 'e', 'l', 'l', 'o', 'h', 'e', 'l', 'l', 'o', 'h', 'e', 'l', 'l', 'o']
</pre>
</div>
</li>