※以下の企画です
今回は引き続き9章をやっていきます。
「クラス」の後半戦で、継承とかがメインテーマになってきそう。
ちなみに書籍自体はこれで、付録を抜いた全159ページ中109ページ目になるので、2/3がおわったことになる。
ボリューム少なめの書籍だとはいえ、ちゃんとしっかりやり込んでここまで読み込めているだけでも自分にとってはすごいことだと思うw
9章 学習内容まとめ
継承
またしても自分のレベルがバレてしまうので、あまり言いたくないのだが…
継承って何?何に使うの?
という感じ。
なので、まずは定義から追っていく。
継承とは
Pythonにおける継承とは、既存のクラス(親クラス)の機能や特性を引き継ぎ、新しいクラス(子クラス)を作る仕組みとのこと。
ほうほう。なんとなく理解はできる。
書籍に書いてあるコードを写経してちょっと改造したものが以下。
>> class BaseClass:
>> def a(self):
>> print("base class a")
>> def b(self):
>> print("base class b. 継承されてオーバーライドされる関数")
>> class DerivedClass(BaseClass):
>> def b(self):
>> print("derived class b. 継承された!!")
>> base_class = BaseClass()
>> base_class.a()
>> base_class.b()
>> print("")
>> over_ride_test = DerivedClass()
>> over_ride_test.a()
>> over_ride_test.b()
base class a
base class b. 継承されてオーバーライドされる関数
base class a
derived class b. 継承された!!
あああなるほど。
基となるクラス(基底クラス)を引数にクラスの宣言をすれば、基底クラスの諸々を引き継げるということか。
そして新しいクラス(派生クラス)の方で、同じメソッド名で上書き(オーバーライド)すればそっちが適用されると。
めちゃくちゃ理解できた。というか今までなんで全然知らなかったんだコレ。
と思ったが、普段クラスの処理を全然しないポンコツエンジニアの僕には、そもそも継承の具体的な活用例が思いつかないのかもしれない。
なのでGPTにも色々教えてもらった。
継承の活用例
1. UIコンポーネントの再利用
- 例: ボタンやテキストボックスなどのGUI
-
親クラス: 基本的なUIコンポーネント(例:
Widget
)- 共通機能: レイアウト、クリックイベント、スタイル設定
-
子クラス: 特定のコンポーネント(例:
Button
,TextBox
)-
Button
: クリック時の動作をオーバーライド -
TextBox
: 文字入力機能を追加
-
- 活躍場面: Webアプリやデスクトップアプリで、統一した設計でコンポーネントを作りたいとき
2. ゲーム開発
- 例: キャラクターの種類ごとの動作
-
親クラス:
Character
クラス- 共通機能: 移動、攻撃
-
子クラス: キャラクタータイプごとの特殊能力(例:
Warrior
,Mage
)-
Warrior
: 近接攻撃ロジック -
Mage
: 魔法使用ロジック
-
- 活躍場面: キャラクターの数が増えても、新しいクラスを追加するだけで拡張が可能
ふむふむ。たしかに必要だw
自分の書いているコードの中でも効果的に活用出来る場面は探していきたい。
イテレータ
ちょっと細かい用語等が正しくない可能性も高いのだが…
クラスオブジェクトは、イテレータを使うためにはイテレータオブジェクトなるもの生成する必要がある。っぽいことが書籍内の書かれている。
それはiter()
というビルトイン関数を用いて生成する。
iter()
でを使うと、イテレータオブジェクトが返ってきて、そこで__next__()
というメソッドも定義されるらしい。こいつが次の要素を反復的に参照するためのミソになっていて、要素を参照しつくすとStpoIteration例外
を送出して、for文はこれを検知してループを止めると。
__next__()
メソッドは、next()
関数をコールすることでコールできるとのこと。
めちゃくちゃ大事な内容だけど、書籍の内容だけだとちょっとフワフワした理解になってしまう。
なのでいつものごとく、GPTに補完説明をしてもらった。
イテレータの定義
- イテレータとは、
__iter__()
メソッドと__next__()
メソッドを持つオブジェクト - イテラブル(Iterable)なオブジェクト(例: リスト、タプル、文字列など)は、
iter()
を呼ぶことでイテレータを取得できる
iter()
- イテラブルオブジェクトに対して
iter()
を呼ぶと、そのオブジェクトのイテレータが生成される - イテラブルオブジェクトが自分自身をイテレータとして返す場合もある(例: ファイルオブジェクト)
next()
- イテレータに対してnext()を呼ぶと、
__next__()
メソッドが実行され、次の要素が返される - 取り出せる要素がなくなると、
StopIteration
例外が発生する
> # イテラブルオブジェクト(リスト)
> numbers = [1, 2, 3]
> # イテレータを生成
> iterator = iter(numbers)
> # イテレータを使って要素を取得
> print(next(iterator)) # 出力: 1
> print(next(iterator)) # 出力: 2
> print(next(iterator)) # 出力: 3
> print(next(iterator)) # StopIteration例外が発生
1
2
3
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-22-618fa4e774c7> in <cell line: 11>()
9 print(next(iterator)) # 出力: 2
10 print(next(iterator)) # 出力: 3
---> 11 print(next(iterator)) # StopIteration例外が発生
StopIteration:
まぁなんとなくちょっとわかった気がする。__next__()
のこととかもっとちゃんと深く知りたいなと思いつつ、いつまでもここから進めなくなりそうなのでクラスの話に戻す。
クラスとイテレータ
イテレータオブジェクトとして、.next()
などを使える状態にするには、__next__()
と__iter__()
があれば良いということがわかっている。
つまり、クラスの中にそれらのメソッドを実装してしまえば良い。
>> class Reverse:
>> def __init__(self, data):
>> self.data = data
>> self.index = len(data)
>>
>> def __iter__(self):
>> return self
>>
>> def __next__(self):
>> if self.index == 0:
>> raise StopIteration
>> self.index = self.index - 1
>> return self.data[self.index]
上記がクラスの定義で、ちゃんと、__next__()
と__iter__()
が存在している。
__iter__()
は存在だけしてもらうためにself
を返すようにして、ミソは__next__()
メソッドにある。
__next__()
にてindex
を変動させるようなシンプルな処理を施すことで、next()
関数による繰り返し処理を実現する。
ここまで見ればさすがに理解が深まった。なるほどなるほど、思ってたよりシンプル。
実行結果は以下。
>> rev = Reverse('spam')
>> iter(rev)
>>
>> for char in rev:
>> print(char)
m
a
p
s
まとめ
クラスの勉強というよりも、イテレータの勉強になった気がする。
イテレータが登場した序盤でここまで説明しなかったのは、まだその段階だとメソッドの概念がわからないからなのかもしれない。
こういう細かい原理原則を理解できると、ドヤ顔で他人に説明をできるようになるので助かる。
次の投稿も頑張っていきます。