-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy patheffective3.html
1615 lines (1441 loc) · 64.6 KB
/
effective3.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 37 to 51) — Dreaming 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="/">Dreaming 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 37 to 51)</h1>
<p class="meta">
<time datetime="2020-08-21T00:00:00-04:00" pubdate>Fri 21 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="#orgd747d52">1. Chapter 5: クラスとインタフェース</a>
<ul>
<li><a href="#orga548f59">1.1. Item 37: ビルトインタイプを何重にもネストさせるより(複数の)クラスを作れ</a></li>
<li><a href="#org350d297">1.2. Item 38: シンプルなインタフェースにはクラスでなく関数を受け入れよ</a></li>
<li><a href="#org3046877">1.3. Item 39: オブジェクトをgenericに作るためには@classmethodポリモーフィズムを使え</a></li>
<li><a href="#org97d2918">1.4. Item 40: superを使って親クラスを初期化せよ</a></li>
<li><a href="#org4e9ded0">1.5. Item 41: Mix-inクラスを使って機能をcomposeすることを考えよ</a></li>
<li><a href="#org113dcca">1.6. Item 42: プライベートなアトリビュートよりもパブリックな方が良い</a></li>
<li><a href="#orge9829f9">1.7. Item 43: カスタムコンテナタイプを作るにはcollections.abcを継承せよ</a></li>
</ul>
</li>
<li><a href="#org2ac7e9a">2. Chapter 6: メタクラスとアトリビュート</a>
<ul>
<li>
<ul>
<li><a href="#org2389ddb">2.0.1. メタクラスの定義</a></li>
<li><a href="#org1df1e55">2.0.2. typeが持つ別の顔</a></li>
<li><a href="#orgce359ae">2.0.3. typeの正体</a></li>
</ul>
</li>
<li><a href="#orgdc22e9d">2.1. Item 44: SetterやGetterメソッドよりもアトリビュートを普通に使え</a></li>
<li><a href="#orgf5021fc">2.2. Item 45: アトリビュートのリファクタリングよりも @property を使え</a></li>
<li><a href="#org59ee075">2.3. Item 46: 再利用可能な@propertyメソッドとしてデスクリプターを使え</a></li>
<li><a href="#org0591756">2.4. Item 47: Lazyアトリビュートのために__getattr__, __getattribute__や__setattribute__を使え</a></li>
<li><a href="#orgdaf16a8">2.5. Item 48: __init_subclass__を使ってサブクラスをvalidateせよ</a></li>
<li><a href="#org6352ebd">2.6. Item 49: __init_subclass__を使ってクラスを登録せよ</a></li>
<li><a href="#orga0a514b">2.7. Item 50: __set_name__を使ってクラスアトリビュートをannotateせよ</a></li>
<li><a href="#org683aff6">2.8. Item 51: クラス拡張を組み合わせるために、メタクラスよりもクラスデコレーターを使え</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="outline-container-orgd747d52" class="outline-2">
<h2 id="orgd747d52"><span class="section-number-2">1</span> Chapter 5: クラスとインタフェース</h2>
<div class="outline-text-2" id="text-1">
</div>
<div id="outline-container-orga548f59" class="outline-3">
<h3 id="orga548f59"><span class="section-number-3">1.1</span> Item 37: ビルトインタイプを何重にもネストさせるより(複数の)クラスを作れ</h3>
<div class="outline-text-3" id="text-1-1">
<p>
コードを拡張して以下のような複雑なコードになったら、複数のクラスを使うようにリファクタリングした方が良いです。
</p>
<ul class="org-ul">
<li>ディクショナリを含むディクショナリ</li>
<li>3以上の個数を持つタプルを値として持つディクショナリ</li>
<li>複雑にネストした他のビルトインタイプ</li>
</ul>
<p>
長いタプルはnamedtupleを使う手もありますが、namedtupleには制限があります。
</p>
<ul class="org-ul">
<li>デフォルト値が指定できない</li>
<li>数値のインデックスやiterationでアトリビュート値がアクセスできる</li>
</ul>
</div>
</div>
<div id="outline-container-org350d297" class="outline-3">
<h3 id="org350d297"><span class="section-number-3">1.2</span> Item 38: シンプルなインタフェースにはクラスでなく関数を受け入れよ</h3>
<div class="outline-text-3" id="text-1-2">
<pre class="example">
current = {'green': 12, 'blue': 3}
</pre>
<p>
このディクショナリに、
</p>
<pre class="example">
increments = [('red', 5), ('blue', 17), ('orange', 9)]
</pre>
<p>
このタプルのリストを加えて、
</p>
<pre class="example">
result = {'green': 12, 'blue': 20, 'red': 5, 'orange': 9}
</pre>
<p>
を得たいとします。
</p>
<p>
次の関数は、ステートフルなclosureを使って追加した(=ミスした)色の数をカウントしつつ、上記のことを行います。
</p>
<div class="org-src-container">
<pre class="src src-python">def increment_with_report(current, increments):
added_count = 0
def missing():
'''カウントを+1して0を返す'''
nonlocal added_count # ステートフルなclosure
added_count += 1
return 0
result = defaultdict(missing, current) # closureを仕込む
for key, amount in increments:
result[key] += amount
return result, added_count
</pre>
</div>
<p>
以前調べたとき、closuresは以下の定義でした。
</p>
<ul class="org-ul">
<li>関数と、そのインナー関数がある</li>
<li>インナー関数は、外側の関数で定義された変数を参照する</li>
<li>外側の関数は、インナー関数を戻り値として返す</li>
</ul>
<p>
上記の例では最後の要件を満たしていませんが、 <code>defaultdict()</code> にインナー関数 <code>missing()</code> を渡すことで、closureとして機能していることになるようです。
</p>
<p>
また、上の例ではclosureをステートを保持する目的で使っています。これもclosureのユースケースの一つなのですね。
</p>
<p>
しかし、ステートフルなclosureは読みづらいために避けるべきと書いてあります。その代わりにクラスを用意し、更に <code>__call__()</code> を実装することでクラスを関数のように使うことを推奨しています。
</p>
<div class="org-src-container">
<pre class="src src-python">class BetterCountMissing:
def __init__(self):
self.added = 0
def __call__(self):
self.added += 1
return 0
counter = BetterCountMissig()
result = defaultdict(counter, current) # counterを関数として渡す
for key, amount in increments:
result[key] += amount
</pre>
</div>
</div>
</div>
<div id="outline-container-org3046877" class="outline-3">
<h3 id="org3046877"><span class="section-number-3">1.3</span> Item 39: オブジェクトをgenericに作るためには@classmethodポリモーフィズムを使え</h3>
<div class="outline-text-3" id="text-1-3">
<p>
えー、MapReduceって何でしたっけ。処理をたくさんのworkersにばらまいて、結果を統合していくやつでしたよね。この本は前提知識が高度すぎて、大変読みづらいです。ぼちぼち見ていきましょう。
</p>
<p>
これはインタフェース的な、継承されることを前提としたクラスですね。
</p>
<div class="org-src-container">
<pre class="src src-python">class InputData:
def read(self):
raise NotImplementedError
</pre>
</div>
<p>
パスを渡されてリードする処理を実装した子クラスです。
</p>
<div class="org-src-container">
<pre class="src src-python">class PathIputData(InputData):
def __init__(self, path):
super().__init__()
self.path = path
def read(self):
with open(self.path) as f:
return f.read()
</pre>
</div>
<p>
これもインタフェース的workerクラス。
</p>
<div class="org-src-container">
<pre class="src src-python">class Worker:
def __init__(self, input_data):
self.input_data = input_data
self.result = None
def map(self):
raise NotImplementedError
def reduce(self, other):
raise NotImplementedError
</pre>
</div>
<p>
行数をカウントする処理を入れ込んだworker子クラス。
</p>
<div class="org-src-container">
<pre class="src src-python">class LineCountWorker(Worker):
def map(self):
data = self.input_data.read()
self.result = data.count('\n')
def reduce(self, other):
self.result += other.result
</pre>
</div>
<p>
次は、ディレクトリをリストして、その中にあるファイルをリードできる <code>PathInputData</code> オブジェクトをyieldするジェネレーター関数を定義します。
</p>
<div class="org-src-container">
<pre class="src src-python">import os
def generate_inputs(data_dir):
for name in os.listdir(data_dir):
yield PathInputData(os.path.join(data_dir, name))
</pre>
</div>
<p>
そしてworkersを複数用意するところ。ワーカーとして <code>LineCountWorker</code> を渡しています。
</p>
<div class="org-src-container">
<pre class="src src-python"># 引数input_listはPathInputDataをyieldするジェネレーター
def create_workers(input_list):
workers = []
for input_data in input_list:
workers.append(LineCountWorker(input_data))
return workers
</pre>
</div>
<p>
次は用意したworkersを実行するところ。Pythonでスレッド使うやり方習いましたっけ? まあ読めばわかるのでいいや。 <code>thread.join()</code> は作ったスレッドの実行が終わるまで待つ(ブロックされる)ということ。
</p>
<div class="org-src-container">
<pre class="src src-python">from threading import Thread
def execute(workers):
threads = [Thread(target=w.map) for w in workers]
for thread in threads: thread.start()
for thread in threads: thread.join()
first, *rest = workers
for worker in rest:
first.reduce(worker)
return first.result
</pre>
</div>
<p>
おー、なんか格好いいです。これがMapReduceの実装なのですね。先頭の worker に全ての結果を集約しています。
</p>
<p>
最後にこれらをまとめます。
</p>
<div class="org-src-container">
<pre class="src src-python">def mapreduce(data_dir):
inputs = generate_inputs(data_dir) # ジェネレーターを返す
workers = create_workers(inputs)
return execute(workers)
</pre>
</div>
<p>
<code>generate_inputs</code> は <code>PathInputData</code> を一つずつ返す iterator を作り、 <code>create_workers</code> でこれらを割り振ったワーカーのリストを用意して、 <code>executee</code> で mapreduce します。うーん、格好いい!
</p>
<p>
ここまで理解したところで、やっと本節のテーマに入ります。導入が長すぎる。。。
まずは、上記のやり方に対してダメ出しです。問題は、パーツの結びつきがお互いに強すぎて、一つ変更するとみんな変更しなくてはいけないこと。独立性が低すぎるということです。
</p>
<p>
これに対する解はクラスメソッドのポリモーフィズムを使うことだと書いてあります。正直言って、意味がわかりません。読み進めましょう。
</p>
<p>
まずはgenericな <code>InputData</code> クラスを定義します。追加したのはconfigパラメーターから設定を読み込むクラスメソッド(のインタフェース)。意味深です。
</p>
<div class="org-src-container">
<pre class="src src-python">class GenericInputData:
def read(self):
raise NotImplementedError
@classmethod
def generate_inputs(cls, config):
raise NotImplementedError
</pre>
</div>
<p>
<code>PathInputData</code> クラスでこれを継承します。
</p>
<div class="org-src-container">
<pre class="src src-python">class PathInputData(GenericInputData):
def __init__(self, path):
super().__init__()
self.path = path
def read(self):
with open(self.path) as f:
return f.read()
@classmethod
def generate_input(cls, config):
data_dir = config['data_dir']
for name in os.listdir(data_dir)
yield cls(os.path.join(data_dir, name))
</pre>
</div>
<p>
別関数だった <code>generate_input</code> を <code>PathInputData</code> にクラスメソッドとして組み込んでいます。
</p>
<p>
<code>yield</code> の行は、 <code>PathInputData</code> のインスタンスを作って、その引数としてconfigのディレクトリにある各ファイルのパスを渡しています。 <code>@classmethod</code> を付けてクラスメソッドを用意することで、 <code>__init__()</code> を使わない別のやり方でコンストラクターを定義することができる、ということでした。
</p>
<p>
また、genericなクラスではなく具体的な子クラスでそのクラスメソッドを実装することで、子クラスに合わせたフレキシブルな初期化ロジックを入れ込むことができます。
</p>
<p>
そしてgenericなworkerクラスです。
</p>
<div class="org-src-container">
<pre class="src src-python">class GenericWorker:
def __init__(self, input_data)
self.input_data = input_data
self.result = None
def map(self):
raise NotImplementedError
def reduce(self, other):
raise NotImplementedError
@classmethod
def create_workers(cls, input_class, config):
workers = []
for input_data in input_class.generate_inputs(config):
workers.append(cls(input_data))
return workers
</pre>
</div>
<p>
こちらでも別関数だった <code>create_workers</code> をクラスメソッドとして組み込んでいます。今回のポイントはクラスメソッドを使ったポリモーフィズムとのことですが、 <code>create_workers</code> をジェネリックなworkerクラスに組み込んだところを言っているのでしょうか。
</p>
<p>
以前は <code>mapreuce</code> 関数で呼び出していた <code>generate_inputs</code> はこの中で呼び出されるようになります。つまり、 <code>create_workers</code> メソッドにおいて <code>PathInputData</code> インスタンスの生成と、それを組み込んだ <code>GenericWorker</code> インスタンスの生成を行っています。
</p>
<p>
<code>LineCountWorker</code> は <code>GenericWorker</code> を継承します。他は変更なし。
</p>
<div class="org-src-container">
<pre class="src src-python">class LineCountWorker(GenericWorker):
def map(self):
...
def reduce(self, other):
...
</pre>
</div>
<p>
<code>mapreduce</code> 関数は引数としてconfigを取ります。 <code>generate_inputs</code> の呼び出しは <code>create_workers</code> に含まれたのでここからは無くなっています。シンプルになりました。
</p>
<div class="org-src-container">
<pre class="src src-python">def mapreduce(worker_class, input_class, config):
workers = worker_class.create_workers(input_class, config)
return execute(workers)
</pre>
</div>
<p>
そして、全ての基点がここ。
</p>
<div class="org-src-container">
<pre class="src src-python">config = {'data_dir': tmpdir}
result = mapreduce(LineCountWorker, PathInputData, config)
print(f'There are {result} lines')
>>>
There are 4360 lines
</pre>
</div>
<p>
ところで、独立性が低すぎる件はこれで解決したのでしょうか。クラスのインスタンス生成をクラスメソッドとして組み込んだためスッキリしたとは思いますが、どこが解決されているのか今ひとつわかりません。。。
</p>
<p>
今回の節はかなり難しかったです。意味を理解するのに数時間以上悩みました。
</p>
<p>
追記です。このようなやり方でオブジェクトを作る方法は、static factory patternというようです。
</p>
</div>
</div>
<div id="outline-container-org97d2918" class="outline-3">
<h3 id="org97d2918"><span class="section-number-3">1.4</span> Item 40: superを使って親クラスを初期化せよ</h3>
<div class="outline-text-3" id="text-1-4">
<p>
スーパークラスのコンストラクタを呼ぶときは、親クラスを名前で指定するのでなく、 <code>super.__init__()</code> を指定するように、とのこと。普通にやっていますよね。この節はこれで言いたいことはおしまいですが、更に補足します。
</p>
<p>
二つの親クラスを継承した子クラスがあるとして、その二つの親クラスのどこかの先祖が同じクラスであるような継承をダイヤモンド継承と呼ぶらしいです。ダイヤモンド継承ではその同じ祖先のコンストラクタを複数回実行してしまうことで、副作用が出ることがあります。
</p>
<p>
例えば共有する先祖クラスにおいて、親クラスで操作する変数の初期化をしている場合は、一つの親クラスがその変数の初期化及び値を操作した後に、別の親クラスのコンストラクタでもう一度初期化してしまう場合がありえます。
</p>
<p>
しかし、 <code>super</code> を使うと一つの祖先クラスのコンストラクターを1回しか実行しないことを保証してくれます。このあたりのルールは method resolution order (MRO)という仕様にて定義されています。
</p>
<p>
<code>super</code> によるMROを使ったコンストラクターは、先祖クラスのコンストラクターを実行する順序が、ぱっと見の感覚と異なる場合があることに注意。
</p>
</div>
</div>
<div id="outline-container-org4e9ded0" class="outline-3">
<h3 id="org4e9ded0"><span class="section-number-3">1.5</span> Item 41: Mix-inクラスを使って機能をcomposeすることを考えよ</h3>
<div class="outline-text-3" id="text-1-5">
<p>
Mixinsは知りませんでした。<a href="https://www.residentmar.io/2019/07/07/python-mixins.html#:~:text=A%20mixin%20is%20a%20class,this%20feature%E2%80%94and%20nothing%20else.&text=Mixins%20are%20a%20safe%20form%20of%20multiple%20inheritance.">ここ</a>にわかりやすい定義があります。
</p>
<blockquote>
<p>
A mixin is a class that defines and implements a single, well-defined feature. Subclasses that inherit from the mixin inherit this feature—and nothing else.
</p>
</blockquote>
<p>
継承させることを目的に、ある一つの機能だけを定義、実装したクラスで、 <code>xxxMixin</code> のような名前になるそうです。クラスを定義するときに、メインとなる親クラス一つと、複数のmixinクラスを継承するような使い方をします。
</p>
<pre class="example">
class SomeClass(Parent, AaaMixin, BbbMixin, CccMixin):
</pre>
<p>
のような感じで。これは便利そうです。Javaのインタフェースがこれに相当するのでしたっけ。
</p>
<p>
この中に出てくるバイナリーツリーのコードの意味がピンときません。
</p>
<div class="org-src-container">
<pre class="src src-python">class BinaryTree(ToDictMixin):
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
</pre>
</div>
<p>
<code>ToDictMinxin</code> はディクショナリ(リストや相当機能を持つクラスを含む)をたどってPythonのディクショナリに変換する <code>to_dict</code> メソッドを実装したmixinです。
</p>
<div class="org-src-container">
<pre class="src src-python">tree = BinaryTree(10,
left=BinaryTree(7, right=BinaryTree(9)),
right=BinaryTree(13, left=BinaryTree(11)))
print(tree.to_dict())
>>>
{'value': 10,
'left': {'value': 7,
'left': None,
'right: {'value': 9, 'left': None, 'right': None}},
'right': {'value': 13,
'left': {'value': 11, 'left': None, 'right': None},
'right': None}}
</pre>
</div>
<p>
ここにコードスニップを書いて眺めていたら、ようやく理解できました。
Kindleデバイスでこの本を読むのは無理があります。。。
</p>
<p>
また、
</p>
<div class="org-src-container">
<pre class="src src-python">import json
class JsonMixin:
@classmethod
def from_json(cls, data):
kwargs = json.loads(data)
return cls(**kwargs)
def to_json(self):
return json.dumps(self.to_dict())
</pre>
</div>
<p>
このクラスメソッド <code>from_json()</code> がわかりづらかったです。
<code>data</code> はjsonエンコードしてシリアライズされたデータで、これをディクショナリに変換(デコード)して <code>kwargs</code> に入れます。そしてこれを引数に当該クラスのオブジェクトを作成して戻します。 <code>JsonMixin</code> は、当該クラスをjsonにエンコードする機能と、逆にデコードしてクラスに戻す機能を付与する mixin でした。
</p>
<p>
これはスタティックファクトリーパターン、、、とは違うのかな。
</p>
</div>
</div>
<div id="outline-container-org113dcca" class="outline-3">
<h3 id="org113dcca"><span class="section-number-3">1.6</span> Item 42: プライベートなアトリビュートよりもパブリックな方が良い</h3>
<div class="outline-text-3" id="text-1-6">
<p>
タイトルの通りです。
</p>
<ul class="org-ul">
<li>プライベートアトリビュートは頑張れば子クラスからアクセスできてしまう</li>
<li>プライベートアトリビュートを親クラスから更にその親クラスに移した場合など、子クラスで無理にアクセスしようとしていると名称が変わってアクセスできなくなる</li>
<li>プライベートの仕組みを使ってアクセス制御するよりも、注意事項をドキュメントに書いた方がよい</li>
<li>唯一プライベートアトリビュートを使って良いのは、名前のコンフリクトを避けたい場合</li>
</ul>
</div>
</div>
<div id="outline-container-orge9829f9" class="outline-3">
<h3 id="orge9829f9"><span class="section-number-3">1.7</span> Item 43: カスタムコンテナタイプを作るにはcollections.abcを継承せよ</h3>
<div class="outline-text-3" id="text-1-7">
<p>
これもタイトルの通りです。
</p>
<p>
<code>list</code> や <code>dict</code> 等のPythonで定義されているコンテナタイプを継承してクラスが作れるとは知りませんでした。
</p>
<p>
<code>list</code> 等に用意されている便利なメソッドは多く、一から作るのは大変なので、 <code>collections.abc</code> を継承するとよい、ということでした。
</p>
</div>
</div>
</div>
<div id="outline-container-org2ac7e9a" class="outline-2">
<h2 id="org2ac7e9a"><span class="section-number-2">2</span> Chapter 6: メタクラスとアトリビュート</h2>
<div class="outline-text-2" id="text-2">
<p>
メタクラスって何ですか? StackOverflowの<a href="https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python">what-are-metaclass-in-python</a>に、ものすごく詳しくてわかりやすい解説がありました。
</p>
</div>
<div id="outline-container-org2389ddb" class="outline-4">
<h4 id="org2389ddb"><span class="section-number-4">2.0.1</span> メタクラスの定義</h4>
<div class="outline-text-4" id="text-2-0-1">
<p>
メタクラスはクラスを作るクラスです。クラスはそのクラスの実体(オブジェクト)がどのように振る舞うかを規定しますが、メタクラスはクラスがどう振る舞うかを規定します。クラスはメタクラスのインスタンスです。
</p>
<p>
そういう意味で、クラスはオブジェクトです。オブジェクトは以下の特徴を持ちます。
</p>
<ul class="org-ul">
<li>それを変数にアサインできる</li>
<li>コピーできる</li>
<li>それにアトリビュートを追加できる</li>
<li>関数のパラメーターとして渡すことが出来る</li>
</ul>
</div>
</div>
<div id="outline-container-org1df1e55" class="outline-4">
<h4 id="org1df1e55"><span class="section-number-4">2.0.2</span> typeが持つ別の顔</h4>
<div class="outline-text-4" id="text-2-0-2">
<p>
ご存じのように、 <code>type</code> には <code>type(1)</code> で <code><type 'int'></code> を、 <code>type("1")</code> で <code><type 'str'></code> のように返す機能がありますが、その他に全く別のアビリティーを持ちます。それは、クラスを動的に作る機能です。
</p>
<p>
この機能の使い方は以下です。
</p>
<pre class="example">
type(name, bases, attrs)
# name: クラスの名前
# bases: ペアレントクラスのタプル(空でもよい)
# attrs: アトリビュートの名前と値を持つディクショナリ
</pre>
<p>
例えば、
</p>
<pre class="example">
class Foo():
bar = True
</pre>
<p>
は次のように書けます。
</p>
<pre class="example">
Foo = type('Foo', (), {'bar':True})
</pre>
<p>
目からウロコです。なんだかすごくないですか!?
</p>
<p>
メソッドを定義することも出来ます。
</p>
<pre class="example">
def echo_bar(self):
print(self.bar)
</pre>
<p>
Fooクラスを継承したFooChildを作ります。
</p>
<pre class="example">
FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
</pre>
<p>
これが、Pythonがキーワード <code>class</code> を見つけるとメタクラス <code>type</code> を使って行うことだそうです。
</p>
</div>
</div>
<div id="outline-container-orgce359ae" class="outline-4">
<h4 id="orgce359ae"><span class="section-number-4">2.0.3</span> typeの正体</h4>
<div class="outline-text-4" id="text-2-0-3">
<p>
<code>__class__</code> のアトリビュートを見ると、タイプがわかります。
</p>
<div class="org-src-container">
<pre class="src src-python">>>> age = 35
>>> age.__class__
<class 'int'>
>>> name = 'bob'
>>> name.__class__
<class 'str'>
>>> def foo(): pass
...
>>> foo.__class__
<class 'function'>
>>> class Bar(object): pass
...
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
</pre>
</div>
<p>
おー、これはわかりやすいです。
</p>
<p>
一歩踏み込んで、 <code>__class__</code> の <code>__class__</code> を見てみると、、、
</p>
<div class="org-src-container">
<pre class="src src-python">>>> age.__class__.__class__
<class 'type'>
>>> name.__class__.__class__
<class 'type'>
>>> foo.__class__.__class__
<class 'type'>
>>> b.__class__.__class__
<class 'type'>
</pre>
</div>
<p>
なんと、全て <code>type</code> になっていました。つまり、メタクラス <code>type</code> はPythonの様々なタイプオブジェクトのタイプなのでした。
</p>
<p>
ここまで踏み込んだところで、ようやく本文に入ります。
</p>
</div>
</div>
<div id="outline-container-orgdc22e9d" class="outline-3">
<h3 id="orgdc22e9d"><span class="section-number-3">2.1</span> Item 44: SetterやGetterメソッドよりもアトリビュートを普通に使え</h3>
<div class="outline-text-3" id="text-2-1">
<p>
getter, setterを用意するのはpythonicでないそうです。
</p>
<p>
<code>@property</code> , <code>@<attr>.setter</code> デコレーターを使うと、アトリビュートの値を普通に参照、 <code>=</code> で設定するときにこれらのメソッドが使われます。Introducing Pythonを見返すまで忘れていましたが。。
</p>
<div class="org-src-container">
<pre class="src src-python">class Duck():
def __init__(self, input_name):
self.hidden_name = input_name
@property
def name(self):
return self.hidden_name
@name.setter
def name(self, input_name):
self.hidden_name = input_name
duck = Duck('No name')
duck.name = 'Donald'
print(duck.name)
>>>
Donald
</pre>
</div>
<p>
注意点として、これらは副作用無く、素早く終わるようにすること、だそうです。
</p>
</div>
</div>
<div id="outline-container-orgf5021fc" class="outline-3">
<h3 id="orgf5021fc"><span class="section-number-3">2.2</span> Item 45: アトリビュートのリファクタリングよりも @property を使え</h3>
<div class="outline-text-3" id="text-2-2">
<p>
<code>@property</code> のアドバンストな使い方は、アトリビュートをその場で計算して返すこと。Linuxの/procと似てますね。
</p>
<p>
本題から外れてまたKindle版の悪口ですが、Click here to view code imageをクリックしてビットマップイメージでコードを見ても、まだインデントが間違っている箇所がたくさんあります。本当にひどい。返品したいくらいです。Kindle版の技術書がひどいのはある程度わかっていましたが、ここまでとは。もう安くても買いません。
</p>
<p>
えーと、leaky bucket(穴の開いたバケツ)です。最初、普通に穴の開いたバケツをイメージして読んだのですが、コードの意味がよくわかりません。検索してみると専門用語のようです。ここで言っているクオータは何でしょう。残量? 使用した量? 残量みたいですね。整理しながら読んでいきましょう。
</p>
<p>
まずはバケツを定義します。
</p>
<div class="org-src-container">
<pre class="src src-python">class Bucket:
def __init__(self, period):
self.period_delta = timedelta(seconds=period)
self.reset_time = datetime.now()
self.quota = 0 # クオータを0に初期化
def __repr__(self):
return f'Bucket(quota={self.quota})'
</pre>
</div>
<p>
これはいいですかね。最初はクオータ0始まりです。
</p>
<p>
次は <code>fill()</code> です。バケツに水を汲みます。
</p>
<div class="org-src-container">
<pre class="src src-python">def fill(bucket, amount):
now = datatime.now()
if (now - bucket.reset_time) > bucket.period_delta:
bucket.quota = 0
bucket.reset_time = now
bucket.quota += amount
</pre>
</div>
<p>
時間が <code>bucket.period_delta</code> よりも長く経過していたら、タイマーをリセットし、クオータを <code>amount</code> に設定して、まだだったら残量に <code>amount</code> を追加しています。何ですかね。 <code>fill()</code> を呼ばれたときだけ一気に漏れる(そしてフィルする)バケツなのでしょうか。何だかWikiで呼んだleaky bucket algorithmの定義と違うような。。。
</p>
<p>
そして、Kindle版でインデントがずれていた <code>deduct()</code> です。
</p>
<div class="org-src-container">
<pre class="src src-python">def deduct(bucket, amount):
now = datetime.now()
if (now - bucket.reset_time) > bucket.period_delta:
return False # この期間にバケツはフィルされていない
if bucket.quota - amount < 0:
return False # 使いたい量がない
bucket.quota -= amount
return True
</pre>
</div>
<p>
あれれ、残量によらず(?)期間にバケツがフィルされていなければ <code>False</code> でリターンするのですね。フィルされずに <code>bucket.period_delta</code> を過ぎていたら、既に漏れてしまっているのでdeductさせません(クオータ(残量)の値は変えないけど)という乱暴な作りなのでしょうか。
</p>
<p>
整理してみましょう。
</p>
<ul class="org-ul">
<li>フィルするときに規定時間 <code>buekct.period_delta</code> 経っていなければ残量に追加量を加える</li>
<li>フィルするときに規定時間経っていれば期間のカウンターをリセットして追加量=残量とする</li>
<li>水を使おうとしたときに残量が必要量あり、規定時間経っていなければ、使わせる</li>
<li>水を使おうとしたときに規定時間経っていたら、(残量がどうあれ)使わせない</li>
</ul>
<p>
やはり、乱暴な作りでした。。。
</p>
<p>
この実装の問題は、そもそもバケツの残量がいくつから始まったのかわからないことだ、と書いてあります。そんなに大層な問題ですかね。。。
</p>
<p>
で、改善版です。
</p>
<div class="org-src-container">
<pre class="src src-python">class NewBucket:
def __init__(self, period):
self.period_delta = timedelta(seconds=period)
self.reset_time = datetime.now()
self.max_quota = 0
self.quota_consumed = 0
def __repr__(self):
return (f'NewBucket(max_quota={self.max_quota}, '
f'quota_consumed={self.quota_consumed})')
@property
def quota(self):
return self.max_quota - self.quota_consumed
</pre>
</div>
<p>
<code>quota</code> アトリビュートを廃止して <code>max_quota</code> と <code>quota_consumed</code> を導入しました。そして <code>@property</code> で廃止した <code>quota</code> アトリビュートを動的に作って返します。今回のポイントは実はここだけですかね。
</p>
<p>
更にsetterの定義です。上で定義した <code>fill()</code> と <code>deduct()</code> がそのまま使えるように配慮しました、と書いてあります。
</p>
<div class="org-src-container">
<pre class="src src-python">@quota.setter
def quota(self, amount):
delta = self.max_quota - amount # 減少量 = 最大量 - 設定値
if amount == 0:
# 0を指定した場合は、新たな期間のためにリセットする
self.quota_consumed = 0
self.max_quota = 0
eilf delta < 0:
# 新たな期間のために残量をフィルする
assert self.quota_comsumed == 0
self.max_quota = amount
else:
# 当該期間において、残量を使う
assert self.max_quota >= self.quota_consumed
self.quota_consumed += delta # 消費した量に減少量を加える
</pre>
</div>
<p>
このメソッドだけ眺めても理解できないので、 <code>fill()</code> や <code>deduct()</code> と付き合わせて読みます。
</p>
<p>
まず <code>amount == 0</code> の時は、 <code>fill()</code> で規定時間経過時のフィルのために、いったん残量をゼロにするところです。確かにこれでOKです。
</p>
<p>
次の <code>delta < 0</code> は、 <code>fill()</code> で残量をゼロした後にフィルするところです。残量をゼロにしたときに <code>quota_consumed = max_quota = 0</code> にしているので、 <code>delta</code> は必ず負になります。
</p>
<p>
それ以外のケースは <code>deduct()</code> された場合です。このメソッドでは残量(quota)に、残量から減少量(<code>amount_deduct</code> とする)を引いた量を代入しているので、これがsetterメソッドで残量に設定する値になるから、setterメソッドの引数を <code>amount_setter</code> と表記すると、、、
</p>
<pre class="example">
self.quota_consumed += delta は、
self.quota_consumed = self.quota_consumed + delta ということなので、
= quota_consumed + (max_quota - amount_setter) self略
= quota_consumed + (max_quota - (max_quota - quota_consumed - amount_deduct))
= quota_consumed x 2 + amount_deduct あれあれ???
</pre>
<p>
うーむ、何をどう間違ったのか。 <code>quota_comsumed</code> が2回足されてしまいました。1回で良かったのに。2時間ほど考えてみましたがわかりませんでした。悔しいけど飛ばして進みます。
</p>
</div>
</div>
<div id="outline-container-org59ee075" class="outline-3">
<h3 id="org59ee075"><span class="section-number-3">2.3</span> Item 46: 再利用可能な@propertyメソッドとしてデスクリプターを使え</h3>
<div class="outline-text-3" id="text-2-3">
<p>
<code>@property</code> の大きな問題は再利用性です。同じようなアトリビュートがたくさんあると、それら全てに <code>@property</code> を用意しなければなりません。更に、無関係のクラスでは再利用できません。
</p>
<p>
<code>@classmethod</code> はクラスメソッドで、そのクラスのオブジェクトを作らなくても使えるメソッドでした。 <code>@staticmethod</code> は知っているような気もしますがクラスメソッドと何が違うのでしたっけ。<a href="https://stackabuse.com/pythons-classmethod-and-staticmethod-explained/">ここ</a>によると、スタティックメソッドは引数としてclsを取らないところがポイントで、クラスのステータスを扱えません。クラスメソッド(やインスタンスメソッドも?)から下請的に使うもののようです。
</p>
<p>
次はデスクリプターです。Pythonのunder the hoodで活躍するもののようです。under the hood話はたまに聞くから良いのであって、これだけ続くとだいぶお腹いっぱいです。が、Real Pythonの<a href="https://realpython.com/python-descriptors/">長い記事</a>を読みます。。。。。 。。。読みました。最後の二つの節は意味が追えなかったので飛ばしましたが。
</p>
<p>
デスクリプターは以下のデスクリプタープロトコルを一つ以上実装したクラスで、デスクリプター機能を実現したい他のクラスにアタッチして使います。
</p>
<div class="org-src-container">
<pre class="src src-python">__get__(self, obj, type=None) -> object
__set__(self, obj, value) -> None
__delete__(self, obj) -> None
</pre>
</div>
<p>
そしてEffective Pythonに戻って話を続けます。あれれ、話の展開がそっくりです。こんなのあり? でもお陰で読みやすいです。
</p>
<p>
以下の例で <code>Grade</code> がデスクリプターです。
</p>
<div class="org-src-container">
<pre class="src src-python">class Grade:
def __get__(self, instance, instance_type):
...
def __set__(self, instance, value):
...
class Exam:
math_grade = Grade()
writing_grade = Grade()
science_grade = Grade()
</pre>
</div>
<p>
<code>@property</code> と同様に、デスクリプターをアタッチした変数は、クラスインスタンスのアトリビュートとしてアクセスできるようになります。上の例では、 <code>Exam</code> のインスタンスを作ったとき、例えば <code>Exam_instance.writing_grade</code> のアトリビュートアクセスで、 <code>Exam</code> にアタッチされているデスクリプター <code>Grade</code> の <code>__get__</code> 、 <code>__get__</code> が使われます。
</p>
<p>
具体例です。
</p>
<div class="org-src-container">
<pre class="src src-python">exam = Exam()
exam.writing_grade = 40
</pre>
</div>
<p>
これは、次のように解釈されます。
</p>
<div class="org-src-container">
<pre class="src src-python">Exam.__dict__['writing_grade'].__set__(exam, 40)
</pre>
</div>
<p>
ここで、 <code>__dict__</code> はPythonの全てのオブジェクトが持っているディクショナリのアトリビュートで、そのオブジェクトの全てのアトリビュートやメソッドが入っています。
例えば、以下のようになります。
</p>
<div class="org-src-container">
<pre class="src src-python">print(Exam.__dict__)
>>>
{'__module__': '__main__', 'math_grade': <__main__.Grade object at 0x10c02afa0>, 'writing_grade': <__main__.Grade object at 0x10c02af70>, 'science_grade': <__main__.Grade object at 0x10c041070>, '__dict__': <attribute '__dict__' of 'Exam' objects>, '__weakref__': <attribute '__weakref__' of 'Exam' objects>, '__doc__': None}
</pre>