はじめに
普段は物流トラックドライバーをしながらPython学習をしている tosane932 です。
僕がPython学習を始めた初期から現在進行形で開発している、
🍞 sales_data_app 🥖
の実際のコードを教材に、Python基礎を学び直しています。
1時限目では、
変数・データ型・比較演算子・if文
を学び直しました。
2時限目では、
リスト・辞書・set・for文
を学び直しました。
3時限目では、
関数・引数・return・関数の呼び出し・型ヒント
を学び直しました。
4時限目では、
モジュール・import・例外・try・except
を学び直しました。
4時限目では、
from prompts import build_sales_prompt
や、
import config
という実コードから、
処理を別ファイルへ分ける
↓
モジュールとして扱う
↓
importする
↓
別の場所から使う
という流れを見ました。
ところが、app.py をさらに読んでいると、
また知らない書き方が出てきます。
class GuestUser(UserMixin):
🐹「……class?」
しかも、その下には、
def __init__(self, dataset_id):
self.dataset_id = dataset_id
とあります。
🐹「今度はselfまで出てきたぞ。」
今回は、このコードを入口にして、
クラス
について学び直していきます。
🍞 Guest Demoを公開しました
sales_data_app のGuest Demoを公開しました。
この記事で扱っている画面を実際に操作しながら、
画面を触る
↓
裏側のコードを見る
↓
Python基礎とつなげる
という形でも確認できます。
https://bakery-salesdata.onrender.com/
Render上で公開しているため、初回アクセス時は画面が表示されるまで少し時間がかかる場合があります。
これまでの基礎をつないで見てみます
これまで、
month = 9
のような変数や、
def build_sales_prompt(...):
のような関数を見てきました。
今回のコードにも、
値を持つ
↓
処理する
↓
必要な場所で使う
という、これまで見てきたPythonの基礎が入っています。
ただ今回は、それらが、
class GuestUser(UserMixin):
というclassの中にまとまっています。
その中には、
is_admin = False
is_guest = True
という値や、
def __init__(self, dataset_id):
self.dataset_id = dataset_id
という処理があります。
🐹「今まで別々に見てきたものが、classの中に集まってる?」
ここから、
クラス
↓
インスタンス
↓
__init__
↓
self
↓
属性
と、一つずつ見ていきます。
🍞 それでは5時限目
今回のテーマ
「クラス・インスタンス・init・self・属性とは?」
今回学び直すのは5つです。
1. 📦 クラスとは?
まずはクラスです。
Pythonでは、
class
を使ってクラスを定義できます。
簡単な例なら、
class Bread:
name = "メロンパン"
のように書けます。
ここでは、
class
↓
これからクラスを定義する
Bread
↓
クラスの名前
:
↓
ここからクラスの中身
という形です。
クラスは初心者向けのイメージとして、
同じような特徴を持つものを作るための設計図
と考えることがあります。
例えば、
パンという設計図
↓
名前
価格
種類
のように、関連する情報をまとめておくイメージです。
🐹「設計図そのものと、実際のパンは別ってこと?」
ここが今回の大事なところです。
クラスは、
こういうデータや処理を持つものですよ
という形を定義します。
そこから実際に作られたものが、
インスタンス
です。
🍞 sales_data_appのクラスを見てみる
現在の app.py には、
class GuestUser(UserMixin):
is_admin = False
is_guest = True
def __init__(self, dataset_id):
self.dataset_id = dataset_id
というコードがあります。
今回まず注目するのは、
class GuestUser
です。
名前を見ると、
Guest
↓
ゲスト
User
↓
利用者
なので、
Guest Demoを利用するユーザーを表すクラス
だと読み取れます。
その中には、
is_admin = False
is_guest = True
があります。
つまり、
管理者ではない
Guestである
という特徴を持っています。
🐹「GuestUserって名前だけじゃなく、中にもGuestとしての情報を持ってるんだ。」
(UserMixin)は今回は保留
実際のコードは、
class GuestUser(UserMixin):
となっています。
GuestUser の後ろに、
(UserMixin)
があります。
これはクラスの継承に関係する部分です。
ただし今回は、
classとは?
インスタンスとは?
selfとは?
を中心に学び直すため、
UserMixin や継承についてはここでは深入りしません。
今回はまず、
class GuestUser(...):
を見たら、
GuestUserというクラスを定義している
と読めればOKとします。
継承については、実コードから必要になったときに改めて掘ります。
図1:画面ではGuest Demoを開始するボタンが表示されています。このボタンからGuest Demoを開始すると、内部では
GuestUserクラスからGuest利用者を表すインスタンスが作られます。
2. 🥐 インスタンスとは?
クラスを定義しただけでは、
まだ実際のGuest利用者そのものができたわけではありません。
ここで登場するのが、
インスタンス
です。
簡単な例なら、
class Bread:
pass
というクラスから、
bread = Bread()
と書くことができます。
この、
Bread()
によって作られたものが、
Bread クラスのインスタンスです。
イメージすると、
Bread
↓
パンを表すクラス
Bread()
↓
クラスから実際のオブジェクトを作る
bread
↓
作られたインスタンスを変数へ入れる
となります。
🐹「設計図がclassで、そこから作った実物がインスタンスか。」
今回はまず、このイメージで進めます。
🍞 sales_data_appではどこで作っている?
実際の app.py には、
return GuestUser(guest_dataset.id)
というコードがあります。
ここに、
GuestUser(...)
が登場しています。
これは、
GuestUserクラスからインスタンスを作っている
部分です。
さらに、
guest_dataset.id
という値を渡しています。
GuestUserクラス
↓
GuestUser(guest_dataset.id)
↓
GuestUserのインスタンスを作る
という流れです。
🐹「3時限目の関数呼び出しに見た目が似てるね。」
たしかに、
build_sales_prompt(sales_summary)
と、
GuestUser(guest_dataset.id)
は、どちらも、
名前(...)
という形をしています。
ただし今回は、
GuestUser(...)
によって、
GuestUserクラスのインスタンス
を作っています。
returnもまた出てきた
実コードは、
return GuestUser(guest_dataset.id)
です。
ここには3時限目で学んだ、
return
もあります。
つまり、
GuestUserのインスタンスを作る
↓
return
↓
作ったGuestUserを返す
という形です。
🐹「また前の授業が混ざってきた。」
今回も、新しく学ぶものだけでコードができているわけではありません。
3. 🚪 __init__とは?
次は、
__init__
です。
GuestUserクラスには、
def __init__(self, dataset_id):
self.dataset_id = dataset_id
があります。
見慣れない、
__init__
という名前です。
これは、
インスタンスを作るときに、そのインスタンスを初期化するために呼ばれる処理
です。
例えば、
class Bread:
def __init__(self, name):
self.name = name
というクラスがあったとします。
そして、
bread = Bread("メロンパン")
とすると、
Bread("メロンパン")
↓
インスタンスを作る
↓
__init__が呼ばれる
↓
"メロンパン"を受け取る
という流れになります。
🍞 GuestUserでは何を受け取る?
実コードでは、
def __init__(self, dataset_id):
self.dataset_id = dataset_id
です。
そしてインスタンスを作る場所では、
GuestUser(guest_dataset.id)
となっています。
ここをつなげると、
guest_dataset.id
↓
GuestUser(guest_dataset.id)
↓
__init__(self, dataset_id)
↓
dataset_idとして受け取る
となります。
🐹「3時限目の引数とつながってる!」
そうです。
3時限目では、
def greet(name):
に対して、
greet("モグラくん")
と値を渡しました。
今回は、
def __init__(self, dataset_id):
に対して、
GuestUser(guest_dataset.id)
から値が渡されています。
4. 🐹 selfとは?
ここで、
def __init__(self, dataset_id):
の中に、
self
があります。
🐹「dataset_idは分かる。でもselfって誰だ?」
今回のコードでは、
self
は、
今扱っているGuestUserのインスタンス自身
を表しています。
最初は、
「このGuestUser自身のこと」
くらいのイメージで読んでみます。
簡単な例なら、
class Bread:
def __init__(self, name):
self.name = name
があります。
そして、
melon_bread = Bread("メロンパン")
とした場合、
そのインスタンス自身に、
self.name
というデータを持たせます。
イメージすると、
"メロンパン"
↓
nameとして受け取る
↓
self.nameへ入れる
↓
このBreadインスタンスが
自分の名前を持つ
という感じです。
🍞 GuestUserのself
実コードへ戻ります。
def __init__(self, dataset_id):
self.dataset_id = dataset_id
右側の、
dataset_id
は、
__init__ が受け取った値です。
左側の、
self.dataset_id
は、
このGuestUserインスタンス自身が持つdataset_id
です。
GuestUser(guest_dataset.id)
↓
dataset_idとして受け取る
↓
self.dataset_id = dataset_id
↓
そのGuestUser自身が
dataset_idを持つ
となります。
🐹「同じdataset_idって名前だけど、左右で役割が違うんだね。」
そういうことになります。
右と左を分けて見る
この1行、
self.dataset_id = dataset_id
だけを見ると少し分かりにくいので、分けてみます。
右側
dataset_id
↓
__init__が受け取った値
そして、
左側
self.dataset_id
↓
そのインスタンス自身に持たせる属性
です。
1時限目で見た、
month = 9
と同じように、
右側の値を
左側へ入れる
という基本も残っています。
🐹「selfが付いても、代入そのものは1時限目と同じなのか。」
5. 🏷️ 属性とは?
最後は属性です。
先ほど出てきた、
self.dataset_id
の、
dataset_id
は、GuestUserインスタンスが持つ属性です。
属性は簡単に考えると、
そのオブジェクトが持っているデータ
です。
例えば、
class Bread:
def __init__(self, name):
self.name = name
なら、
bread = Bread("メロンパン")
としたあと、
bread.name
のように属性へアクセスできます。
🍞 GuestUserの属性は実際にあとで使われている
sales_data_app では、
GuestUserインスタンスに、
self.dataset_id = dataset_id
として保存した値を、
後の処理で使っています。
実コードには、
principal.dataset_id
があります。
つまり、
GuestUserを作る
↓
self.dataset_idへ
Guest用DatasetのIDを保存
↓
後の処理で
principal.dataset_id
↓
そのGuestが使えるDatasetを確認する
という流れになっています。
🐹「最初に保存したdataset_idを、ちゃんとあとで使ってるんだ。」
ここまでつながると、
self.dataset_id = dataset_id
が単なる書き方ではなく、
Guestごとに必要な情報を持たせるための処理
だと見えてきます。
図2:Guest Demoの日次売上入力画面です。画面上では各商品の販売数を入力・更新しますが、内部では
GuestUserが持つdataset_idを使って、そのGuestが利用できるデータを判定しています。
クラスに直接書かれた属性もある
GuestUserには、
is_admin = False
is_guest = True
もあります。
これは、
self.dataset_id
とは少し書き方が違います。
class GuestUser(UserMixin):
is_admin = False
is_guest = True
のように、クラスの中へ直接書かれています。
これらはクラス属性と呼ばれます。
一方、
self.dataset_id = dataset_id
として各インスタンスに持たせるものは、
インスタンス属性
です。
今回のGuestUserなら、
is_admin = False
is_guest = True
↓
GuestUserとして共通する情報
self.dataset_id
↓
そのGuestごとに持つ情報
という違いがあります。
🔗 5つを全部つなげてみる
ここまで学んだ、
クラス
インスタンス
__init__
self
属性
を、sales_data_app の実コードでつなげてみます。
まず、
class GuestUser(UserMixin):
is_admin = False
is_guest = True
def __init__(self, dataset_id):
self.dataset_id = dataset_id
があります。
そして、
GuestUser(guest_dataset.id)
として使います。
流れにすると、
class GuestUser
↓
GuestUserクラスを定義する
↓
GuestUser(guest_dataset.id)
↓
GuestUserのインスタンスを作る
↓
__init__
↓
初期化処理が行われる
↓
dataset_id
↓
GuestのDataset IDを受け取る
↓
self.dataset_id = dataset_id
↓
そのGuestUser自身へ
dataset_idを持たせる
↓
principal.dataset_id
↓
後の処理から属性を使う
という流れです。
🐹「5個別々の単語じゃなくて、今回も1本につながった。」
👤 Guest Demoの中では何が起きている?
Guest Demoでは、
Guestごとに分離されたDatasetを使います。
コードを今回の範囲だけで単純化すると、
Guest用Datasetを取得する
↓
guest_dataset.idがある
↓
GuestUser(guest_dataset.id)
↓
GuestUserのインスタンスを作る
↓
__init__が呼ばれる
↓
self.dataset_idへ保存する
↓
GuestUserをreturnする
↓
後の処理で
principal.dataset_idを読む
↓
そのGuest用Datasetを確認する
という流れです。
利用者から見ると、
Guest Demoへ入る
↓
商品を見る
↓
売上を入力する
↓
ダッシュボードを見る
という普通の画面操作です。
でも内部では、
「このGuestは、どのDatasetを使う人なのか?」
という情報を持つ必要があります。
そこで、
GuestUser
というクラスと、
self.dataset_id
という属性が使われています。
🐹「画面だけ見てたら、classなんてどこにも見えないもんね。」
今回も、
画面
↓
コード
↓
Python基礎
をつなげることで、裏側の仕組みが少し見えてきました。
👀 同じファイルにはまだ知らないものがある
GuestUserクラス全体を見ると、
今回触れていないコードもあります。
@property
def id(self):
...
や、
class GuestUser(UserMixin):
の、
UserMixin
です。
🐹「まだ掘れる穴が残ってるぞ。」
まだあります。
今回すべてを一度に覚えようとすると、
クラス
インスタンス
self
__init__
属性
メソッド
継承
@property
デコレータ
...
と一気に増えてしまいます。
そこで5時限目では、
クラス
↓
インスタンス
↓
__init__
↓
self
↓
属性
までにします。
残ったものは、
実コードを読み進めて必要になったときに掘る
ことにします。
📝 5時限目まとめ
今回は、sales_data_app のGuest Demoに関わる実コードから、
クラス
データや処理をまとめるための仕組み。
class GuestUser(...):
インスタンス
クラスをもとに実際に作られたオブジェクト。
GuestUser(guest_dataset.id)
__init__
インスタンスを作るときに初期化する処理。
def __init__(self, dataset_id):
self
そのインスタンス自身を表す。
self.dataset_id
属性
オブジェクトが持っているデータ。
self.dataset_id
実際のコードでは、
GuestUserクラスを定義する
↓
GuestUserのインスタンスを作る
↓
__init__でdataset_idを受け取る
↓
self.dataset_idへ保存する
↓
後の処理から属性を使う
とつながっています。
そして今回も、
変数
関数
引数
return
など、これまで学んだPython基礎がまた登場しました。
🤔 自分で確認してみる
最後に、今回の内容を自分でも確認してみます。
Q1
Pythonでクラスを定義するときに使うキーワードはどれでしょう?
A. class
B. return
C. except
🅰️答えを見る
A. class
です。
例えば、
class GuestUser:
のように書きます。
Q2
次のコードでは、何をしているでしょう?
GuestUser(guest_dataset.id)
A. if文を実行している
B. GuestUserのインスタンスを作っている
C. モジュールをimportしている
🅰️答えを見る
B. GuestUserのインスタンスを作っている
です。
その際に、
guest_dataset.id
を渡しています。
Q3
次の、
def __init__(self, dataset_id):
の __init__ は、今回どのような処理として学んだでしょう?
🅰️答えを見る
インスタンスを作るときに呼ばれる、
初期化のための処理
です。
Q4
次のコードの、
self
は何を表しているでしょう?
self.dataset_id = dataset_id
🅰️答えを見る
今回扱っている、
そのインスタンス自身
を表しています。
Q5
次のコードで、右側の dataset_id は何でしょう?
self.dataset_id = dataset_id
🅰️答えを見る
__init__(self, dataset_id)
で受け取った値です。
その値を、
self.dataset_id
へ保存しています。
Q6
次の、
self.dataset_id
は今回何と呼んだでしょう?
A. 属性
B. 例外
C. モジュール
🅰️答えを見る
A. 属性
です。
GuestUserインスタンスが持っているデータです。
Q7
次の2つでは、どんな違いがあるでしょう?
is_guest = True
self.dataset_id = dataset_id
🅰️答えを見る
今回のGuestUserでは、
is_guest = True
はクラス側に定義されたクラス属性です。
一方、
self.dataset_id = dataset_id
は、それぞれのGuestUserインスタンスが持つインスタンス属性です。
今回のイメージでは、
GuestUserとして共通する情報
↓
is_guest
Guestごとに違う情報
↓
dataset_id
となります。
🐹 5時限目を終えて
今回は、
クラス・インスタンス・init・self・属性
を学び直しました。
最初に、
class GuestUser(UserMixin):
is_admin = False
is_guest = True
def __init__(self, dataset_id):
self.dataset_id = dataset_id
だけを見ると、
🐹「class、カッコ、self、アンダーバーだらけ……。」
となりそうです。
でも今回見る範囲だけに分解すると、
class GuestUser
↓
GuestUserというクラス
GuestUser(...)
↓
インスタンスを作る
__init__
↓
インスタンスを初期化する
self
↓
そのインスタンス自身
self.dataset_id
↓
そのインスタンスが持つ属性
と、一つずつ意味を見ることができました。
さらに、
return GuestUser(guest_dataset.id)
から、
GuestUserのインスタンスを作る
↓
dataset_idを渡す
↓
__init__が受け取る
↓
self.dataset_idへ保存する
↓
作ったGuestUserをreturnする
という流れまでつなげることができました。
そして後の処理では、
principal.dataset_id
として、
保存した属性を使っています。
今回も、
変数
関数
引数
return
など、前の授業で学んだものがまた登場しました。
新しい基礎を学ぶ
↓
以前の基礎がまた出てくる
↓
別の使われ方を見る
↓
少しずつつながる
今回もこの繰り返しです。
🐹「1時限目から掘ってきた穴、とうとうclassまで来たぞ。」
もちろん今回も、記事を1本書いただけで、
「クラスを理解した!」
とは考えません。
これから実コードを読んでいて、
class
を見つけたら、
これは何を表すクラスだろう?
と考える。
__init__
を見つけたら、
インスタンスを作るとき、
何を受け取っているんだろう?
と考える。
self.xxx
を見つけたら、
このインスタンスは、
どんな情報を持っているんだろう?
と考える。
そんな読み方が少しずつできれば、
以前なら、
class GuestUser(UserMixin):
で止まっていたコードも、
少しずつ読めるようになりそうです。
そして、このすぐ近くにはまだ、
UserMixin
@property
メソッド
デコレータ
など、知らないものが残っています。
🐹「次の穴もすぐ隣にあるじゃん。」
まだまだ基礎の学び直しは続きます。
次の時限も、🍞 sales_data_app の実コードを教材にして掘っていきます🐹
📚️ 基礎学習シリーズ
🏫1時限目
「変数・データ型・比較演算子・if文とは?」
🏫2時限目
「リスト・辞書・set・for文とは?」
🏫3時限目
「関数・引数・return・関数の呼び出し・型ヒントとは?」
🏫4時限目
「モジュール・import・例外・try・exceptとは?」
🏫5時限目
「クラス・インスタンス・init・self・属性とは?」


