【デザインパターン with Python】1-3. Classを使ってみよう: Class定義と__init__とself

記事タイトルとURLをコピーする

はじめに

こんにちは。孔子の80代目子孫兼アプリケーションサービス部の孔です。前回の記事ではオブジェクト指向プログラミングについて見てみましたね。そして、PythonではClassというものを使ってオブジェクトを作成するとの話をしました。

今回から3回に分けて、そのClassの使用方法を具体的に見てみましょう。まずはClassの定義方法からです。

Classの定義

まずは復習になります。オブジェクトは「データ」と「処理」の集まりであること、そしてPythonではオブジェクトをclassという予約語を使って定義するとの話をしました。余談ですが、「データ」のことを「属性(=プロパティ、Property)」、「処理」のことを「メソッド(Method)」と呼ぶことが多いです。今後、「属性」や「メソッド」という言葉が出てきたら、それぞれ何かのクラスの中で定義された「データ」と「処理」を指してるんだなと思ってください。

それではオブジェクトを作ってみましょう。今回は飲食店で、各テーブルごとの注文に関するオブジェクトを作ってみます。飲食店には、テーブルが複数あって、それぞれのテーブルで注文やお客様の管理をしています。今回は、テーブルごとの注文に関するオブジェクトは以下のデータを必要とするオブジェクトとします。

  • テーブル番号
  • 注文した料理
  • 滞在時間

そして、以下の処理をすることとします。

  • 注文を追加する
  • 滞在時間を出力する
  • お会計金額を出力する

上記の実装を、以下のようにすることができそうです。コードの分析はこの後に続きますのでまずは軽く読んでみてください。

import time
 
class Table:
    # テーブルごとの注文に関するオブジェクト
    def __init__(self, table_num: int):
        # テーブル番号, 注文した料理, 滞在時間のデータを作る
        self.table_num = table_num
        self.order = []
        self.start_time = time.time()
 
    def add_order(self, order: list):
        self.order.extend(order)
        return None
 
    def get_stay_time(self):
        return time.time() - self.start_time
 
    def billing(self):
        amount = self.__calculate_amount()
        return amount
 
    def __calculate_amount(self):
        menu = Menu.get_menu()
        amount = 0
        for ordered in self.order:
            amount += menu[ordered]
        return amount
 
class Menu:
    # メニューに関するオブジェクト
    @classmethod
    def get_menu(cls):
        menu = {
            "beef": 1000,
            "chicken": 800,
            "pork": 900
        }
        return menu
 
# お客様が10番テーブルに着席したよ!
table = Table(10)
 
# 注文内容は豚肉と牛肉だよ!
table.add_order(["pork", "beef"])
 
# 着席してからどれくらい時間が経ったのか確認するよ!
table.get_stay_time()
 
# お会計だよ!
table.billing()

それでは、コードの中身を見てClassは何から定義されるのか見てみましょう。以下の順番で見ていきます。

  • __init__とは
  • selfとは
  • アクセス修飾子(Access Modifier)とは
  • クラスメソッド(Class Method)とは

initとは:コンストラクタの紹介

上から読んでみて、まず普段あまり目にしないものがありますね。

class Table:
    def __init__(self, table_num: int):  # これなに?

コンストラクタと呼ばれるこの__init__ですが、役割は「インスタンスが生成されるときに呼び出される処理」となります。以下のコードをみてください。

class Test:
    def __init__(self):
        print("Testのインスタンスが生成されました")
 
test_1 = Test()
# Testのインスタンスが生成されました
test_2 = Test()
# Testのインスタンスが生成されました
test_3 = Test()
# Testのインスタンスが生成されました

Testクラスからtest_1, 2, 3といったインスタンスを生成するたびに__init__で定義した処理が走り、print関数が実行されています。インスタンスを作成する際には初期値を設定したり、決まった共通の処理をやるなど、最初にやっておく必要がある処理があることがあります。その際には__init__からコンストラクタを定義しましょう!

selfとは:インスタンス自身を指す慣用句

その次に目立つのがこのselfというものです。

import time
 
class Table:
    # テーブルごとの注文に関するオブジェクト
    def __init__(self, table_num: int):
        # テーブル番号, 注文した料理, 滞在時間のデータを作る
        self.table_num = table_num
        self.order = []
        self.start_time = time.time()
 
    def add_order(self, order: list):
        self.order.extend(order)
        return None
 
    def get_stay_time(self):
        return time.time() - self.start_time
 
    def billing(self):
        amount = self.__calculate_amount()
        return amount
 
    def __calculate_amount(self):
        menu = Menu.get_menu()
        amount = 0
        for ordered in self.order:
            amount += menu[ordered]
        return amount

selfは、「インスタンス自身」という意味となります。例えばself.table_numはそのインスタンス自身の「table_num」というデータ、billingメソッドで使われているself.__calculate_amount()はインスタンス自身が持っている「__calculate_amount」というメソッドを呼び出す、といった意味になります。

これのおかげで、各インスタンスごとにデータを管理することができるようになります。以下のコードをみてください。

class Animal:
    def __init__(self, num_of_legs):
        # num_of_legs = 動物の足の本数
        self.num_of_legs = num_of_legs
 
cat = Animal(4)
cat.num_of_legs
# 4
spider = Animal(8)
spider.num_of_legs
# 8
centipede = Animal(100)  # ムカデ。漢字で「百足」なので一応100に設定
centipede.num_of_legs
# 100

このように、インスタンスを何個生成しても、足の本数が知りたければオブジェクトに対して「num_of_legs」を参照すると、それぞれのインスタンスが持っている値を取得することができるようになります。これがselfの役割となります。

クラスの中で定義される全てのメソッドは第一引数にインスタンス自身を指す引数を指定します。そのため、別にselfじゃなくmyselfwatashiと命名してもいいですが、慣用的にselfと付けることが一般的です。

最後に

今回、結構な分量になってきましたので、ここで一旦区切ります!次回、続きで以下の紹介をします。

  • アクセス修飾子とは
  • クラスメソッド(Class Method)とは

シリーズ一覧

孔 允培 (執筆記事の一覧)

アプリケーションサービス部 ディベロップメントサービス課

孔子の80代目子孫