IT・技術研修ならCTC教育サービス

サイト内検索 企業情報 サイトマップ

研修コース検索

コラム

ゼロから歩くPythonの道

CTC 教育サービス

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  Microsoft Azure  Docker  Kubernetes 

第32回 特殊メソッド「__init__」を使って、動く円のクラスを作ってみよう (菱沼佑香) 2022年5月

こんにちは、吉政創成 菱沼です。
今回も「いちばんやさしいPython入門教室(大澤 文孝氏著) 」を片手に勉強していきます。

「クラス」と「オブジェクト」の概念を学ぶということで、その準備段階として、tkinterのcanvas上に動く円を作る方法について学んできました。
そして前回はそれまでに学んだことを踏まえ、オブジェクトやクラスの概念、インスタンスとオブジェクトの違いついて学び、ざっくりではありますがまとめてみました。
今回からは、前回学んだクラスとオブジェクトの概念を踏まえ、クラスを使った動く円のプログラムのつくり方を学んでいきます。今回はまず、クラスの部分を作成し、次回、メソッドを学びたいと思います。

クラスで使うコンストラクタ「__init__」は特殊メソッドの一つ

まずはテキストから引用です。

引用-----
P.215
1つの円は以下の5つのデータを持っています。
 x座標、y座標、xの移動量、yの移動量、色
そこでまずは、この5種類のデータをオブジェクト内部で管理できるようにし、そのためのクラスを記述します。クラス名は、何でもかまいませんが、ここでは仮に「Ball」という名前にします。Pythonでクラスを作る場合は、次のような書式で記述します。
>>> class クラス名:
>>>       クラスの定義内容
ここでは、次のようにBallクラスを作ります。

fig01

ここで定義した「__init__」というのは、最初にオブジェクトを作るときに呼び出される特殊な関数で「コンストラクタ(constructor)」と呼ばれます。コンストラクタは、オブジェクトの状態を、最初の状態にするときの処理をするのに使います。
-----

まずクラスの具体的な作り方について一旦おいておき...。
新たに出てきた「__init__(コンストラクタ)」周りについて確認してみたいと思います。
この「__init__」のような「__(アンダースコア×2)」が前後につけられているものをサンプルコードが掲載されているWebサイトでよく見かけたりしますよね。
こういった「__」が前後につくものはオブジェクトの挙動を決めたり、オブジェクトの振る舞いを変えたりしたいときに使われる特殊メソッドと呼ばれるもので、処理の実行時に自動的に呼び出されるそうです。

「__init__」の役割としてはインスタンスの初期化ですが、これ以外にインスタンスの生成、比較、型変換、演算といった役割を持つ特殊メソッドが複数存在しており、それぞれ呼び出されるタイミングが異なります。
例えば「__init__」の場合はインスタンスの生成後に読み込まれるそうですが、インスタンスの生成をする「__new__」はインスタンス生成前に、足し算(+)を行うときに呼び出されるメソッド「__add__」は+演算子を使用されるタイミングで呼び出されるといった具合です。

特殊メソッドを利用するメリットとしては、オブジェクトの振る舞いをあらかじめ決めることになるので、コードの量を減らせたり、後々の管理が楽になったりといったことがあるそうです。

参考サイト:
3.3. 特殊メソッド名|Python3.9 公式ドキュメント
Pythonのクラスでカスタマイズできる特殊メソッド
Pythonにおけるコンストラクタ__init__の使い方|特徴も解説
【Python入門】クラス利用時の特殊メソッド一覧(サンプルコード付き)
Pythonの特殊メソッド一覧を備忘録としてまとめてみた。

ということで、特殊メソッドの概要についてはいったん、ここまでにしておきます。
今回使用することになる「__init__」は特殊メソッドの中でも比較的よく使われるもののようです。クラスでの具体的な使い方について見ていきたいと思います。

クラスを使って作りたい円の設計図(ひな形)を書いてみる

まず、前々回の時は円の仕様を「リスト」を使って、下図のように書きました。(3つの円)

<リストで用意する場合>

fig02

<今回使用するクラスの記述(引用文から)>

fig03

今回使用するクラスは作りたいものの設計図やひな形といった存在です。
「__init__」でインスタンスの初期化をしつつ、インスタンス変数(self.xなどの部分)を宣言することで、Ballクラスを使って作りたい円のひな形が決められていきます。
そして、上図を見て頂ければわかるように、リストの時のように具体的な値はクラスの中には書かれません。

では数値の部分はいったいどこで、どうやって定義するのか。
引用文にあるコード内には細かい記述がされていませんので、一旦、サンプルコード全体の中から、メソッド(円の動作を決める部分)の記述を飛ばして、さらにそのあとに続けられていた記述の中からそれっぽい部分を取り出してみたのが下図になります。

fig04

(ちなみに今回は円を一つだけ作るものになっているようですが、複数の円にする方法もありました。複数にする方法についてはメソッド部分をやった後に書いてみることにして、今回は一旦横に置いておきます...。)

さて、この変数bに入れられている値がBallクラスのdxやdyといった部分に順番に当てこまれるんだろうなあという事は想像できますが、まずはどんな流れで入れられるかを確認してみたいと思います。

クラスとインスタンスの定義までの流れ

まずはテキストから。

-----
引用 P.216
Ballクラスを作ったとき、このクラスからオブジェクト――Ballオブジェクト――を作るには、次のように記述します。このようにすると、Ballオブジェクトができ、変数bに代入されます(変数bは任意の名称であり、どのような変数でもかまいません)。
 b = Ball( 400, 300, 1, 1, “red”)
この時内部では、コンストラクタ(クラス内に記述した__init__という名前の関数)が実行され一連の処理が実行されます。この結果、Ballオブジェクトの中には「x」「y」「dx」「dy」「color」という名前の変数ができ、そこに引数に渡された値が代入されます。オブジェクトの内部にある変数を「インスタンス変数」と呼びます。
---------

ということで、これに続いてテキストでは図が入ります。テキストの図を自分が理解しやすいようにちょっと付け足して整理してみました。
(ちなみにテキストではBallオブジェクトと書かれていますが、正確にはBallインスタンスなんじゃないかなと思ってみたり...)

fig05

(図中および下に続く文章の表現が正確かは自信ありません。理解するためのざっくり感な文章だとご理解頂ければ幸いです。)

まず、[インスタンス名=クラス名(引数)](ここでは[b =Ball (引数)])を書くことで、Ballクラスを使って実体化したいインスタンス(ここではb)の値が定義されます。

インスタンスの定義がなされた後、Ballクラスで決められたインスタンス変数にbに入っている値を代入していくことで、bという円がインスタンス化される準備が整いました。
今回の場合は円の動作を決めるメソッドがこの後に追加されるので、実体化させるための命令文は別で記述されることになるのではないかと思います。

これでクラスと円の定義は終わりました。

インスタンス自身を指す引数「self」は省略できるけど省略しない方が良い

ところでここで出てきた[self]ですが、これは「__init__」の第一引数として入る(入れた方がいい)存在だそうです。ではこのselfとは何なのか?テキストではこう書かれています。

----
引用P.217
この先頭の「self」は、「オブジェクトを指し示す」という約束になっています。
以降、このクラスには、ほかにもいくつかのメソッドを作っていくことになりますが、「先頭の引数は、いつも操作対象のオブジェクトが渡される」という点は、共通です。
このようにして渡されたselfに対して、「self.x」や「self.y」のように、「.」でつなげて任意の変数名を記述すると、それを変数として使えます。このようなオブジェクトの内部の変数のことを「インスタンス変数」と言います。
関数の1つ目の引数は、慣例的にselfという名前が使われますが、self以外の名前でもかまいません。たとえば、「def __init__(s,x,y,dx,dy,color):」のように定義してもかまいません。この場合、インスタンス変数を参照するための書式は、「s.x」や「s.y」のようになります。
----

...selfを書かなくてもできるなら、そもそも要らなくない?と疑問が...。ということで、調べつつ聞きつつわかったことをまとめてみます。
テキストでは「self」は「オブジェクトを指し示すもの」と書かれていますが、まず、正確にはオブジェクトというより、「インスタンス自身」なのだそうです。

引用にもあるようにselfを第一引数にすることは慣例であり、省略して別の記述の仕方でもできるということなのですが、これは、たとえselfが省略されていたとしても、selfがインスタンス自身であることには変わりはありません。そのため、実際にクラスが使う時には裏側で自動的にselfとして値が渡るようになっているため、selfを省略しても動かせるという意味だそうです。(見えないけど実は一緒にいる...それがself...)

いずれにせよ、別の記述をせずにselfで書いておいた方が、後々、ほかの人も理解しやすく、読みやすいコードになるため、selfを第一引数として使用した方が良いということでした。

より詳しく書いてくださっているサイトを見つけましたのでご紹介します。ご興味がある方は是非ご一読ください。

参考サイト:
クラスメソッドの引数にselfを使う理由
Pythonのselfはなぜ必要なのかをじっくり考察してみた
クラスと self と __init__
なんで self を書かないといけないの?

ということで、今回はこちらで終了です。次回はメソッドの部分を学びたいと思います。
お付き合いいただきありがとうございました。

 


 

 [IT研修]注目キーワード   Python  UiPath(RPA)  最新技術動向  Microsoft Azure  Docker  Kubernetes