🔙
🔝

オブジェクト指向

オブジェクト指向プログラミングの超基本的な書き方がわかるようになります。👍

Chatpter.1 - オブジェクト指向とは

オブジェクト指向とは

  • オブジェクト(Object) とは、「物」という意味です。

  • suspicious object (サスピシアス・オブジェクト) で「不審物」といいます。(関係無い)

  • 現実にある「物」の構造の様な、現実の要素を参考にプログラムを構築していきます。


  • プログラムの構築のしかたは、大きく3つに分類されます。

  • 手続き型
    プログラムは先頭から順に実行されていきます。
    従来の書き方です。
    関数型
    機能の1つ1つを関数に分類し、関数に引数を与えて実行していく構造です。
    「関数(の機能)」が中心となり、値の受け渡しをして処理されます。
    メインプログラムから関数に引数を与えて指示をし、関数の中から更に関数を呼び出しあいながらプログラムが実行されていきます。
    オブジェクト指向 (クラス型)
    『値』が中心となり、値を操作するための機能(メソッド)を使って値が操作されていきます。
    手続き型、関数型で散乱しがちだった値を一括管理することができるようになります。
    詳しくは追々説明していきます。
  • オブジェクト指向を本やネットで学ぼうとすると、大概『車』や『ゲーム』など身近にある「物」に例えて説明されています。

  • しかし、プログラムの!オブジェクト指向の!ことを知ろうとしているのにいきなり車の話されても困りますよね。しかも全然伝わってこないし。

  • 理由は様々ですが、オブジェクト指向は多くの人がつまづきます。わからなすぎて習得を諦める人もいるようです。


  • オブジェクト指向の構造は手続き型と比べると大きく異なります。

  • そして追及していくと奥が深くて完成形がありません。哲学を超えて、もはや芸術です。

  • そんな姿形が定まらないオブジェクト指向の全貌を掴むことではなく、まずは『ピンとくる』くらいを目標にして説明を進めていきます。


  • 『オブジェクト指向とは』の説明にも届かない内容になってしまいました。あせらないあせらない。

Chatpter.2 - クラス型

基本は手続き型、あとは流れで

手続き型

手続き型

  • このプログラム例を使っていきます。

  • operand = '++-+*/+-+*'
    total = 0
    for i, op in enumerate(operand, start=1):
        if op == '+':
            total += i
        elif op == '-':
            total -= i
        elif op == '*':
            total *= i
        else:
            total //= i
    
        print(total)
    
    10 + 1 = 1 31 + 2 = 3 03 - 3 = 0 40 + 4 = 4 204 * 5 = 20 320 // 6 = 3 余り 2 103 + 7 = 10 210 - 8 = 2 112 + 9 = 11 11011 * 10 = 110

    1 から 10 までを、変数 op の算術演算子を前から順に使って計算して total に足していく流れです。

関数型

関数型

    def addition(total, a):
        return total + a
    
    def subtraction(total, a):
        return total - a
    
    def multiplication(total, a):
        return total * a
    
    def division(total, a):
        return total // a
    
    def setup():
        calc_func = {'+': addition,
                     '-': subtraction,
                     '*': multiplication,
                     '/': division
        }
        operand = '++-+*/+-+*'
    
        return calc_func, operand
    
    def output_calc(calc_func, operand, total=0):
        for i, op in enumerate(operand, start=1):
            total = calc_func[op](total, i)
            print(total)
        return
    
    
    output_calc(*setup())  # メインプログラムはココ1行だけ
    
    ※ 出力は省略

    関数を目立たせるために、少し複雑な書き方をしました。

    calc_func は、算術演算子 + - * / をキーにして、値を関数型の関数名にしています。


  • output_calc() 内の2行目の部分の変化は次の通りです。

    1回目のループで、i1 の時

    calc_func = {'+': addition,
                 '-': subtraction,
                 '*': multiplication,
                 '/': division
    }
    operand = '++-+*/+-+*'
    

    def output_calc(calc_func, operand, total=0):
        for i, op in enumerate(operand, start=1):
            total = calc_func[op](total, i)  # ← ココ
    
    op = '+'
    total = calc_func['+'](total, i)
    
    calc_func['+'] = addition ← 関数型の関数名
    total = addition(total, i)
    
    total = addition(0, 1)
    
    ↓ 0 + 1 の戻り値 1 が返る
    total = 1
    
  • メインプログラムに機能を書くのではなく、関数に値を渡して関数の中で処理をするので、手続き型に比べてプログラムの管理はしやすいのですが、値が散乱しがちになります。

  • プログラムにおいて値が最も大切な要素なのに、最も大切な値がきちんと管理できる構造になっていないのです。

  • これでは「動けばなんでもいくね?」思考が抜け出せず、明確なルールを作らない限り作り手によって作り方が大きく異なってしまいます。

オブジェクト指向で書くと

値は神様です

  • オブジェクト指向の「クラス型」というので書いてみます。

    class Calc:  # クラス宣言
        def __init__(self, operand):  # コンストラクタ
            self.operand = operand
            self.total = 0
            self.calc_func = {
                '+': self.addition,
                '-': self.subtraction,
                '*': self.multiplication,
                '/': self.division
            }
    
        def calc(self):  # メソッド
            for i, op in enumerate(self.operand, start=1):
                self.total = self.calc_func[op](i)
                print(self.total)
        
        def addition(self, a):  # メソッド
            return self.total + a
        
        def subtraction(self, a):  # メソッド
            return self.total - a
        
        def multiplication(self, a):  # メソッド
            return self.total * a
        
        def division(self, a):  # メソッド
            return self.total // a
    
    
    def main():
        calc1 = Calc('++-+*/+-+*')
        calc1.calc()
    
    
    if __name__ == '__main__':
        main()
    

  • このプログラム例では簡単な説明に留めておきます。

  • 意味がわからなくても読むだけ読んでみてください。後程わかりやすい例を使って改めてきちんと解説していきます。

  • 一行目の

    class Calc:
    

    というのが「クラス宣言」です。「クラス型」という型で生まれます。

  • Calc というのが「クラス名」になります。

  • 関数の宣言と同じように、クラスもインデントを付けて範囲を示します。

  • 関数と違って () は不要です。


  • その次の def __init__(self): というのが値を一括管理する場所になります。

  • コンストラクタ」と言います。値の『祭壇』です。

  • 尊ばれるべき値たちをここに祀ります。


  • 次以降にある def というのが、お馴染みの「メソッド」です。

  • def の範囲だけを見れば、関数と同じ書き方をして、同じ働きをします。

  • 関数は関数自体が独立して働くのに対し、メソッドはクラスの中で働きます。


  • 所々に変数の前に書かれている self. というのは、また後で説明します。


  • main()関数の中にある、

        calc1 = Calc('++-+*/+-+*')
        calc1.calc()
    

    というところで、プログラムを実行しています。


  • 最後の、

  • if __name__ == '__main__':
        main()
    

    というのは、プログラムの開始をmain()関数から実行するように指示をする呪文です。

  • 今まで地べたに書いていたプログラムをmain()関数の中に移したので、まずその関数からプログラムをスタートしてくださいという意味の文と捉えてください。

  • __init__, __name__, __main__ どれもアンダースコア2つで挟んでいます。

  • 文を書かずに main() を書くだけでも動きますけど、説明は省きますが、オブジェクト指向ではこのように書いておいてください。

  • ほぼ定型文なので、メインプログラムをmain()関数に収めさえすればこのまま写して問題ありません。

  • この2行を書き忘れるとプログラムが始まらないので注意してください。

  • しょっちゅう忘れますけど。😹

  • それでは実際に動くかどうか試してみましょう。

  • これで動かなかったら笑える。😹
Chatpter.3 - クラス型の解説

学校をつくろう!

3.1 - クラスをつくる - クラス宣言

クラス宣言

  • せっかく「クラス」という言葉が使われているので、学校に例えて説明していきます。

  • まずクラスを作るには「クラス宣言」をします。

    class Student:
    

    この一文を書くだけですが、『宣言』ですので「よし!クラスをつくるぞ!」と宣言しただけになります。

  • これを試しに実行してみると・・・

      File "Main.py", line 2
        
                        ^
    SyntaxError: unexpected EOF while parsing
    

    「言うだけじゃなくてちゃんとつくれよ!!」と言っています。(意訳)

  • 試しに言ってみただけなのに本気で怒ってくるとか、まぁ本気で話を聞いてくれている Python3 にハンパな事を言ったこちらが悪いんですけどね。😓

  • class は関数で言う def みたいなものです。

  • そのあとに続く Student というのが「クラス名」です。
    生徒をつくるので Student というクラス名にしました。

  • クラス名は原則として先頭の文字だけを大文字で書きます。

  • そしてこれ以降のインデントをつけた範囲が Student のクラスとなります。

3.2 - 生徒名簿をつくる - コンストラクタ

コンストラクタ

  • 生徒名簿を作る際の「名前」とか「生年月日」とかを扱う為の変数と値を準備します。

  • コンストラクタの書き方は、次のような感じになります。

    class Student:
        def __init__(self):  # コンストラクタ
            name_1st = None
            name_fml = None
            birth_month = None
            birth_day = None
    

    コンストラクタに値を集約することで値の管理がしやすくなりますが、この部分は値だけでなく、関数と同じようにプログラムを書くこともできます。

  • コンストラクタを作るのは単に値の一括管理が目的というわけではなく、真の役割があるのですが、これは インスタンス のところで説明します。

3.3 - 生徒をつくる - インスタンス

インスタンス

  • インスタンスは、作ったクラスを実際に変数にして使えるようにしたものです。

  • 一般的にクラスは「設計図」、インスタンスは設計図をもとに「実体化」したもの(一個体)と説明されます。

  • 漠然とした『生徒』ではなく、生徒『個人』として受け入れます。

  • 設計図から生まれた一個体なので、クラスからいくらでもインスタンスを作ることができるようになります。

  • 関数の時と同じように、クラスをコピーしてインスタンスを作るイメージです。

  • 関数の時はコピーした関数が見えませんでしたけど、インスタンスは見られます。

  • そして一時的にコピーされ、使い終わったら破棄される関数とは違い、インスタンスは勝手に無くなったりはしません。

  • ここでは生徒名簿も作るので、生徒の個人情報を書き込みながら、新しく作ったクラス(教室)に迎え入れていきます。

  • 登録する情報として、コンストラクタには「名前」「誕生日」の項目を用意しました。

    【生徒1】
    名前  : 赤座 あかり
    誕生日: 7月24日

    class Student:
        def __init__(self):  # コンストラクタ
            name_1st = None
            name_fml = None
            birth_month = None
            birth_day = None
    
    akari = Student()  # インスタンスを作成
    
  • 最後の行で akari という生徒をつくりました。

  • しかし不完全です。生徒の情報がなにもありません。

  • これではいるはずの生徒がいないようになってしまいます。\アッカリーン/

  • これに新しくつくった生徒の情報を書き込んで名簿をつくります。

  • 生徒が持つ個人情報をきちんと登録するためにはこのように書き換えます。

    class Student:
        def __init__(self, name_1st, name_fml, birth_month, birth_day):
            self.name_1st = name_1st
            self.name_fml = name_fml
            self.birth_month = birth_month
            self.birth_day = birth_day
    
    akari = Student('あかり', '赤座', 7, 24)
    

    難しいので1つ1つ説明していきます。

  • まず一番下の一行、

    akari = Student('あかり', '赤座', 7, 24)
    

    で、「赤座あかり」という生徒をクラスに迎い入れます。

  • Student クラスのコンストラクタ(__init__)に、生徒のそれぞれの情報を引数として渡します。

  • コンストラクタは、インスタンスを新しく作る際に、
    一番最初に、一度だけ、必ず実行されるメソッドです。

  • self というのが、インスタンス「自身」を指しています。
    インスタンス自身というのが、あかりちゃん自身です。(以降あかり)

  • __init__(self)というコンストラクタself.name_1stという変数、以降同じ様に self がついているものは全て「あかりの所有物」になります。

  • 名前も誕生日もあかりの情報ですし、コンストラクタもあかりの為に最初に1回だけ実行されます。

  • Chatpter.2 で書いたプログラムの中のメソッドにも、第一引数として self が書かれていました。
    self が付いたメソッドも、ここでは全てあかりが所有するものになります。

  • まとめると、self が付いた「コンストラクタ」「変数」「メソッド」は、すべてあかりが所有するもので、

    akari = Student('あかり', '赤座', 7, 24)
    

    ここで Student に与えられたそれぞれ引数がコンストラクタに渡され、コンストラクタの変数やその値が、akari という変数の中に収められます。

  • ここで生み出された akari が『インスタンス』で、インスタンスの名前のことを『インスタンス名』といいます。この場合のインスタンス名は akari になります。

  • 先程まで、インスタンスのことを変数と呼んでいる所がありましたが、正確には変数ではなく、インスタンスと言います。(ややこしいですね💦 今後もインスタンスと呼んでいきます)

  • インスタンスは複数のインスタンス変数(後述)を持ち、複数のメソッド(関数)を持ちます。

  • 今までは変数には1つの値しか収めることができませんでした。

  • リストや辞書なども1つの値で、その値の中に複数の値(要素)を入れることができる構造になっています。

  • そういう構造をもつ変数を「リスト」「辞書」と呼んでいました。

  • リスト型変数というのは List というクラスのインスタンスです。

  • 辞書型変数というのは Dict というクラスのインスタンスです。

  • 今回の akari という変数は、Student というクラスのインスタンスです。
    akariStudent型変数ということになります。(インスタンスだけど・・・)

3.4 - 個人をつくる - self

self

  • self がついているものは、生徒(インスタンス)が個人的に持っているものを指しています。

  • class Student:
        def __init__(self, name_1st, name_fml, birth_month, birth_day):
            self.name_1st = name_1st
            self.name_fml = name_fml
            self.birth_month = birth_month
            self.birth_day = birth_day
    
    akari = Student('あかり', '赤座', 7, 24)
    
  • コンストラクタの第一引数にある self と、変数の前にある self の二種類があります。

  • この2つは、どちらも生徒(インスタンス)が個人的に持っているものということを示しています。

  • クラスの中にあるコンストラクタやメソッドには、必ず self を第一引数に書かなければなりません。

  • そうしないと、そのコンストラクタやメソッドは自分のクラスのものと認識できず、エラーとなります。

    class Student:
        def __init__(name):
            self.name = name
    
    akari = Student('あかり')
    
    Traceback (most recent call last):
      File "Main.py", line 5, in 
        akari = Student('あかり')
    TypeError: __init__() takes 1 positional argument but 2 were given
    
  • エラーメッセージに詳しく書かれていませんけど、「引数を2つ分渡さないといけないのに、1つ分しか用意されてませんよ」というのは、本来コンストラクタの第一引数に self を書かないといけないのに、それが書かれていなかった為にこのエラーが出たのです。

    class Student:
        def __init__(self, name):  # ← self を書く
            self.name = name
    
    akari = Student('あかり')
    

    これで __init__() はインスタンス用のコンストラクタになりました。

  • self が第一引数にあるせいで引数の位置がずれていますけど、self は無いものとして考えてください。

  • Student('あかり') の第一引数 'あかり' は、そのまま __init__(self, name) の第二引数 name に直結しています。


  • 厳密に言うと、第一引数の self は、引数ではありません。
    よって、第二引数と呼んでいた name が本当の第一引数となります。

  • だから self のせいでずれていても正しく動くのです。

    ※ ただし便宜上、今後も self の位置を第一引数と呼ばせていただきます。

  • なぜ self が必要なのか。簡単に考えると、「個人(インスタンス)」の持ち物の他に「クラスの共有物」というものがあります。

  • 本来個々のインスタンスの所有物(主に「値」)は各インスタンスのものであり、他のインスタンスが直接扱うことができません。

  • クラスの共有物というのは、どのインスタンスからもアクセスでき、すべてのインスタンスで値などが共有されるものです。

  • 掲示物や黒板など、個人の物ではなく誰の物でもなくクラスの誰もが見たり使ったりできる物である。これを cls と書いて示します。(今回は cls は扱いません)

  • 「個人の所有物 (self)」か「クラスの共有物 (cls)」かという事を区別する為のものと考えてください。

  • 変数に付く self ですが、これも個人が持つ変数であることを示しています。

    akari = Student('あかり')
    

    ここで Studentクラスからインスタンスを作成する際に、'あかり' を引数としてコンストラクタに渡しています。


    def __init__(self, name):  # コンストラクタ
        self.name = name
    

    引数として渡された 'あかり' はコンストラクタの name に入ります。

    この時点での name は引数として渡された、ただの変数です。

    この name を、インスタンスが持つ変数 self.name に代入して受け渡しをします。

    self.name の様に self. が付いた変数の事を「インスタンス変数」と言います。

  • インスタンスが持つ変数だからインスタンス変数です。

  • Student('あかり') は「差出人」
    name は「配達人」
    self.name は「受取人」

  • こうしてインスタンスの akari が持つ変数や値がコンストラクタによって作成されます。

  • 作成されたインスタンス変数やその値は、インスタンス自身が存在し続ける限り、無くなることはありません。

  • あかりの個人情報は今後、あかりが所有し続けることになります。

  • そしてあかりの個人情報はここ(あかりのインスタンス内)以外に作成されたり管理されたりすることはありません。

  • 今後あかりの個人情報を参照したり操作したりする場合は、常にあかりが持つインスタンス変数にアクセスして行われます。

  • あかりの個人情報が管理されずにその辺に転がっているということはなく、あかりだけが持ち、必ずあかりを通してその個人情報を扱うことになります。


  • こういう仕組みを作ることで、最も大切な「値」をしっかり管理できるようになるわけです。

3.5 - 生徒の情報の参照

インスタンス変数にアクセスする

  • インスタンスに入っている値を扱う場合、次のような書き方をします。

    class Student:
        def __init__(self, name):  # ← self を書く
            self.name = name   # self.name
    
    akari = Student('あかり')
    
    student_name = akari.name  # akari.name
    print(student_name)
    
    あかり

    インスタンス名(akari)の後に、メソッドの書き方の様に、メソッドの代わりに変数名を書きます。

    つまり、self の所に akari を指定して akari.name と書くわけです。

  • あかりの個人情報を参照してみましょう。

    class Student:
        def __init__(self, name_1st, name_fml, birth_month, birth_day):
            self.name_1st = name_1st
            self.name_fml = name_fml
            self.birth_month = birth_month
            self.birth_day = birth_day
    
    akari = Student('あかり', '赤座', 7, 24)
    
    print('名前:', akari.name_fml + akari.name_1st)
    print('誕生日:', str(akari.birth_month) + '月' + str(akari.birth_day) + '日')
    
    名前: 赤座あかり
    誕生日: 7月24日

    インスタンス変数の self の所に参照したいインスタンス名を外側から指定すれば、これまでの変数と同じように扱うことができます。

3.6 - 自己紹介 (メソッド)

メソッドをつくる

  • メソッドの作り方は関数とほとんど同じです。

  • あかりに自己紹介してもらうメソッドを作ってみます。

    class Student:
        def __init__(self, name_1st, name_fml, birth_month, birth_day):
            self.name_1st = name_1st
            self.name_fml = name_fml
            self.birth_month = birth_month
            self.birth_day = birth_day
    
        def self_introduce(self):
            print('私の名前は', self.name_fml + self.name_1st, 'です。')
            print('誕生日は',
                  str(self.birth_month) + '月',
                  str(self.birth_day) + '日です。')
    
    akari = Student('あかり', '赤座', 7, 24)
    
    akari.self_introduce()
    
    私の名前は 赤座あかり です。
    誕生日は 7月 24日です。
  • 関数とメソッドの書き方の違いは2点あります。

  • 1つめは、第一引数に self を付けます。

  • 2つめは、メソッドの最後は戻り値が無い場合は return は書きません。不必要な文は極力省略して簡素化します。

  • 引数を与えたり、return で戻り値を返したりする場合は関数と書き方は同じです。


  • このメソッドを呼び出しているのが最後の行の akari.self_introduce() です。

  • インスタンス変数を参照する時の様に、メソッド名の前にインスタンス名を書き記すだけで呼び出せます。

  • 引数を与える時は、この () 内に書きます。self の存在は無視して、書き方は関数と全く同じで大丈夫です。


  • インスタンス(クラス)内でメソッドを呼び出すときは、インスタンス変数と同じように self.self_introduce() と、メソッド名の前に self を付けます。

  • 自分自身のメソッドを呼び出すという意味です。

  • ここで注目してほしいのが、メソッド内のインスタンス変数の扱いです。

  • 関数の場合は名前と誕生日の値を関数に引数として与えて関数内で扱えるようにしていましたが、インスタンス内での変数とメソッドは同じ self、つまりあかりのものです。

  • メソッドと呼んでいるこのメソッドは「インスタンスメソッド」と言って、インスタンス変数と同じインスタンスのものです。

  • 変数もメソッドも同じインスタンスの持ち物なので引数を与える必要はありません。

  • その代わりに同じインスタンスの持ち物であるということを示す self をつけます。

  • この self にクラスの外側(メインプログラムや関数など)から akari を指定することで、あかりの名前や誕生日などの情報が扱えたり、メソッドであかりに指示を与えたりすることができるようになります。

  • 扱う値はコンストラクタ内に常に一覧となって一か所にまとまっていて、その値を操作するためのメソッドをその下に作っていく。この書き方をすることでどんなプログラムを作ったとしても同じような見た目になるので誰が見てもわかりやすくなり、管理もしやすくなるというわけです。


  • これまでは生徒一人の情報だけしか扱ってきませんでしたが、次で新たに生徒を増やしてみます。

3.7 - すべての生徒を受け入れる

インスタンスをさらに増やす

  • 今回の例では、インスタンスは「1人の生徒」を指します。

  • あかり以外にも生徒を増やしてみましょう。

    【生徒2】
    名前  : 大室 櫻子
    誕生日: 9月7日

    【生徒3】
    名前  : 古谷 向日葵
    誕生日: 6月16日

    【生徒4】
    名前  : 吉川 ちなつ
    誕生日: 11月6日

    class Student:
        def __init__(self, name_1st, name_fml, birth_month, birth_day):
            self.name_1st = name_1st
            self.name_fml = name_fml
            self.birth_month = birth_month
            self.birth_day = birth_day
    
    akari = Student('あかり', '赤座', 7, 24)
    sakurako = Student('櫻子', '大室', 9, 7)
    himawari = Student('向日葵', '古谷', 6, 16)
    chinatsu = Student('ちなつ', '吉川', 11, 6)
    

    あかりの時と同じ様に、各情報を引数として与えるだけです。
    そしてそれぞれの生徒に認識しやすいインスタンス名を付けてあげるだけで何人でも増やせるようになります。


  • みんなに自己紹介してもらいましょう。

    class Student:
        # コンストラクタ - 各生徒の個人情報
        def __init__(self, name_1st, name_fml, birth_month, birth_day):
            self.name_1st = name_1st
            self.name_fml = name_fml
            self.birth_month = birth_month
            self.birth_day = birth_day
    
        # class の中の def は 「メソッド」
        def self_introduce(self):  # 自己紹介
            print('私の名前は',
                  self.name_fml + self.name_1st,
                  'です。')
            print('誕生日は',
                  str(self.birth_month) + '月',
                  str(self.birth_day) + '日です。')
    
    
    # class の外の def は 「関数」
    def setup():
        # 各生徒(インスタンス)の作成
        akari = Student('あかり', '赤座', 7, 24)
        sakurako = Student('櫻子', '大室', 9, 7)
        himawari = Student('向日葵', '古谷', 6, 16)
        chinatsu = Student('ちなつ', '吉川', 11, 6)
    
        return akari, sakurako, himawari, chinatsu
    
    
    def main():
        akari, sakurako, himawari, chinatsu = setup()
    
        akari.self_introduce()     # あかり の 自己紹介
        sakurako.self_introduce()  # 櫻子 の 自己紹介
        himawari.self_introduce()  # 向日葵 の 自己紹介
        chinatsu.self_introduce()  # ちなつ の 自己紹介
    
    
    if __name__ == '__main__':
        main()
    
    私の名前は 赤座あかり です。
    誕生日は 7月 24日です。
    私の名前は 大室櫻子 です。
    誕生日は 9月 7日です。
    私の名前は 古谷向日葵 です。
    誕生日は 6月 16日です。
    私の名前は 吉川ちなつ です。
    誕生日は 11月 6日です。

  • このようにインスタンス名を付けるだけで、指定した生徒のインスタンスメソッドが呼び出されて自己紹介をしてくれるようになります。

3.8 - 生徒をリストにする

インスタンスをリストに格納する

  • 今度は、生徒ごとに作成したインスタンスをリストに格納して扱ってみます。

    class Student:
        def __init__(self, name_1st, name_fml, birth_month, birth_day):
            self.name_1st = name_1st
            self.name_fml = name_fml
            self.birth_month = birth_month
            self.birth_day = birth_day
    
        def self_introduce(self):  # 自己紹介
            print('私の名前は',
                  self.name_fml + self.name_1st,
                  'です。')
            print('誕生日は',
                  str(self.birth_month) + '月',
                  str(self.birth_day) + '日です。')
    
    
    def setup():
        # 各生徒の個人情報リスト
        students_data = [
            ['あかり', '赤座', 7, 24],
            ['櫻子', '大室', 9, 7],
            ['向日葵', '古谷', 6, 16],
            ['ちなつ', '吉川', 11, 6]
        ]
    
        # インスタンス(生徒)を、リスト内で作成
        students = [Student(*data) for data in students_data]
    
        return students
    
    
    def main(students):
        for student in students:
            student.self_introduce()     # 生徒が順番に自己紹介する
    
    
    if __name__ == '__main__':
        main(setup())
    
    私の名前は 赤座あかり です。
    誕生日は 7月 24日です。
    私の名前は 大室櫻子 です。
    誕生日は 9月 7日です。
    私の名前は 古谷向日葵 です。
    誕生日は 6月 16日です。
    私の名前は 吉川ちなつ です。
    誕生日は 11月 6日です。

    自己紹介のメソッドを実行する際、リストにまとめることによって 文で記述を2行に収めることが出来ました。

    その代わり、誰がどこにいるのかがわかりにくくなりましたので、別途把握できる方法が必要になります。(要素番号 = 出席番号順など)


  • そして学年の各クラスをリストにまとめると学年の名簿に、さらに各学年をリストにまとめると全校生徒の名簿になります。

    【例】nanamori[学年][組][出席番号]

    # 七森中学校1年2組 出席番号1番
    student = nanamori[0][1][0] # 出席番号のリストにクラスの生徒のインスタンスが入っている
    
    print(student.name_fml, student.name_1st)
    
    赤座 あかり

    なんてこともできます。nanamori という変数1つの中に、全校生徒の情報がすべて入っているのです。すごいね。

  • ですので、nanamoriメモリをうっかり紛失してしまったら、まさに大事件です。

Chatpter.4 - まとめ

オブジェクト指向は世界の常識

  • 今回は「値の管理」を目的とした最低限の書き方をしました。

  • まだ cls の事をも説明していませんが、しばらくはクラスの書き方としてこの形式に慣れていってください。

    1. クラスを宣言し、クラスに名前を付ける。
    2. コンストラクタ__init__(self)内で、インスタンスで使う変数や値の初期設定をする。
    3. インスタンスが持つ値を操作する為のメソッドを書く。
  • そしてメインプログラムからは、

    1. コンストラクタに必要な引数を渡し、クラスからインスタンスを生み出す。
    2. 生み出したインスタンスの値を、メソッドを使って操作する。

    この5つを流れをまず習得しましょう。

  • コンストラクタとインスタンスメソッドはセットでなければならないという事はありません。それぞれ独立した役割を持っています。

  • もしまだメソッドを書くところまでの理解が難しいなと感じているならば、最初はクラスをコンストラクタのみにして、その値をメインプログラムからアクセスする書き方にしてみましょう。
    【Chatpter.3.5 - 生徒の情報の参照】の形です。

  • その構造が理解出来たら、あとはゆっくりとメソッド化を試していけば、全体の構造と流れが理解できるようになっていくと思います。

  • オブジェクト指向の世界は広すぎて、一気に学ぼうとすると挫折します。

  • また、形式があるのに柔軟性も高いので、あれこれ本を読んでいっても著者によって考え方に違いがあったりしてかえって混乱します。

  • オブジェクト指向で書かなくてもプログラムは動きますが、オブジェクト指向で書くことで汎用性が広がります。

  • 欠点としては、オブジェクト指向で組むと、どうしてもプログラムが大きくなってしまうことです。

  • print('A') だけのプログラムなのに、これをいちいちクラスを使って書くのもおかしな話ですが、もしこれがあちこちで何度も使うというくらい重要な一文であるならば、クラスを使って書く意味があります。

  • 実感はまだ湧かないと思いますが、これまでに使ってきた便利な「関数」もクラスで書かれています。

  • いつぞやの import random という「モジュール」というものもオブジェクト指向の便利機能です。

  • いままで何気なく使ってきた便利な機能はすべてオブジェクト指向の概念のお陰だったのです。

  • 恩恵を受けるだけでなく、これからは世界の住人『与える側』としてもプログラムが組んでいけるようになっていきましょう。