Python、機械学習

【Python】初心者にわかりやすいクラス(class)とインスタンス(instance)

クラス設定やインスタンス生成のところは、なかなか感覚がつかめなくて頭に入ってきません。

そこで、ドラクエのキャラクターを作ることで、クラスとインスタンスを理解し、使えるようにしたいと思います。

クラスとインスタンス

まずはクラスとインスタンスの説明です。

「クラス」とは、プラスチックの人形

「クラス」とは、ドラクエの登場人物のもととなるものです。

言うなれば、お菓子のおまけについてくる、人間の形をしたプラスチックの人形です。

これに服のシールや帽子のシール、メガネやひげのシールをペタペタ貼ることで、色んなキャラクターを作ることができます。

鎧のシールと剣のシール、そして筋肉のシールと短髪の髪型のシールをつけたら「戦士(男)」になります。

青いローブのシールと十字架が書かれた帽子のシール、ロングヘアーの髪型のシールをつけたら「僧侶(女)」になります。

ピエロの表情のシールと縞々の服のシールをつければ「遊び人」になりますね。

「インスタンス」とは、最終的にできあがるキャラクター

戦士や僧侶、遊び人といった、最終的にできあがるキャラクターを「インスタンス」といいます。

そして、ペタペタ貼ったシールたちが「インスタンス変数」です。

「クラス」に「インスタンス変数」をインプットすると、それに応じた「インスタンス」ができあがる、ということです。

実際に、ドラクエのキャラクターを作ってみる

実際にクラス、インスタンスを使って、ドラクエのキャラクターを作って見たいと思います。

クラス名はCharacterとします。

前述の例では、インスタンス変数は服装やアクセサリのシールでした。

ここでは、名前、職業、レベル(LV)、ヒットポイント(HP)、マジックポイント(MP)をインスタンス変数とします。

まずは初期設定をする(def __init__)

ここはお決まりの書き方だと思ってください。

class Character():
    def __init__(self, name, job, LV, HP, MP):
        self.name = name
        self.job = job
        self.LV = LV
        self.HP = HP
        self.MP = MP

まずは、1行目で「class Character():」として、Character()というクラスを作ります。クラス名の頭文字は大文字にするのが命名規則です。

2行目以降のところで、個性を出すためのインスタンス変数を定義していきます。

2行目は、「def __init__(self, 〇〇, △△, ・・・)」の形を書きます。この赤字はお決まりと思ってください。

〇〇、△△、・・・というのがインスタンス変数です。

ちなみに、__init__はイニットと読みます。

3行目以降で、各インスタンス変数について「self.〇〇 = 〇〇」と書きます。これもお決まりです。

これで、初期設定が終わりました。

※この変数は全て小文字にするのが命名規則ですが、ここでは見やすさ重視ということで大文字も使っていますのでご了承ください。

各キャラクターができることを設定する(def)

例えば、足元を調べるという機能をつけたいなら、下にこのようなコードを付け足します。

    def shiraberu(self):
        print('{}はあしもとをしらべた。
         なんと、かくしかいだんがみつかった!'.format(self.name))    

この機能をshiraberu()という関数名にしています。

コードに書いてあるように、shiraberu()のカッコ内には必ず「self」を入れてください。

これもお決まりです。

そして、関数の中身を書いていきます。

関数の中で使うコンスタンス変数はself.〇〇を使ってください。

キャラクターを作る(インスタンスを生成する)

いよいよ、個々のキャラクターを作っていきます。

「sarii」というキャラクターを作るとします。

名前は「さりい」、職業は「魔法使い」、レベルは15、HPは45、MPは80とします。

sarii = Character('さりい', '魔法使い', 15, 45, 80)

これで「sarii」が完成しました。さりいという名前のキャラクターですね。

キャラクターに足元を調べさせる(関数の実行)

「sarii」に足元を調べさせて見ましょう。

sarii.shiraberu()

さりいはあしもとをしらべた。なんと、かくしかいだんがみつかった!

「sarii」の名前がちゃんと入った状態で実行されていることを確認できました。

もっと関数を増やしてみよう

キャラクターの動作にバラエティをもたせるために、さらに関数を追加していきます。

import pandas as pd

class  Character():
    def __init__(self, name, job, LV, HP, MP):
        self.name = name
        self.job = job
        self.LV = LV
        self.HP = HP
        self.MP = MP
        
    def shiraberu(self):
        print('{}はあしもとをしらべた。
        なんと、かくしかいだんがみつかった!'.format(self.name))    
    
    def status(self):
        display(pd.DataFrame([self.job, self.LV, self.HP, self.MP], 
         index=['職業', 'LV', 'HP', 'MP'], columns=[self.name]))
        
    def level_up(self):
        print('{}はレベルが{}になった!'.format(self.name, (self.LV +1)))
        print('LV: ' + str(self.LV) + '→' + str(self.LV + 1))
        self.LV = self.LV + 1
        
    def use_hyado(self):
        print('{}はヒャドをとなえた!'.format(self.name))
        print('MP : ' + str(self.MP) + '→' + str(self.MP - 10))
        self.MP = self.MP - 10
        
    def damaged_by_slime(self):
        print('スライムのこうげき!{}は3のダメージをうけた!'.format(self.name))        
        print('HP : ' + str(self.HP) + '→' + str(self.HP - 3))
        self.HP = self.HP - 3

追加した関数は以下の4つです。

・各キャラクターのステータスを表示するstatus()関数
・レベルが上がるlevel_up()関数
・ヒャドを唱えるuse_hyado()関数
・スライムの攻撃を受けてダメージを食らうdamaged_by_slime()関数

status()関数でdataframeを使うため、冒頭でPandasをインポートしています。

それでは、「sarii」に実行させてみましょう。

sarii.status()
sarii.level_up()

さりいはレベルが16になった!
LV: 15→16

sarii.use_hyado()

さりいはヒャドをとなえた!
MP : 80→70

sarii.damaged_by_slime()

スライムのこうげき!さりいは3のダメージをうけた!
HP : 45→42

ドラクエっぽくなってきました(笑)。

最後に、もう一度status()関数を実行してステータスを見てみます。

sarii.status()

上で実行したことがしっかり反映され、レベルが上がり、HP、MPが減っていることを確認できました。

別のキャラクターをつくる

「sarii」だけじゃなく「takao」というキャラクター(インスタンス)も作ってみましょう。

takao = Character('たかお', '戦士', 14, 120, 0)

これで「takao」の完成です。

takao.status()

せっかく作ったので、「takao」のレベルを上げてあげましょう。

takao.level_up()

たかおはレベルが15になった!
LV: 14→15

補足(結局、selfとは何だったのか?)

クラス、インスタンスがわかりにくい原因に、selfというのがあります。

一見、何のために存在しているのかわかりません。

しかし、これがないとうまく動きません。

イメージはこんな感じです。selfとは「sarii」とか「takao」が入る変数です。

Character(self, name, job, LV, HP, MP)

Characterというプラスチック人形に、nameシール, jobシール, LVシール, HPシール, MPシールを貼ったものを、selfというキャラクターにしましょう、という意味です。

カッコ内に入っているものはすべて変数です。

ここに具体的な値を当てはめてみます。

「sarii」の例だと、

Character(sarii, 'さりい', '魔法使い', 15, 45, 80)

Characterというプラスチック人形に、’さりい’, ‘魔法使い’, 15, 45, 80を貼ったものを、sariiというキャラクターにしましょう。

「takao」の例だと、

Character(takao, 'たかお', '戦士', 14, 120, 0)

Characterというプラスチック人形に’たかお’, ‘戦士’, 14, 120, 0を貼ったものを、takaoというキャラクターにしましょう。

となります。

このように、シールが決まればキャラクターが決まる、としているのが、Character()の__init__のところです。

class Character():
    def __init__(self, name, job, LV, HP, MP):
        self.name = name
        self.job = job
        self.LV = LV
        self.HP = HP
        self.MP = MP

各シールとキャラクターがひも付けされています。

それで、キャラクターを作るときは、

Character(self, name, job, LV, HP, MP)

という形ではなくて、

self = Character(name, job, LV, HP, MP)

という形にしましょう、となっているわけです。

なんとなくわかりましたでしょうか?

まとめ

クラスとインスタンスのところは、ひとまずこれだけ理解しておけば十分と思います。

あとは、Google検索で調べたり、参考書をめくるなりして都度サーチすれば対応できると思います。

__super__()とか、__call__()とか、時折でてきますので。

それにしても、我ながらドラクエに例えて説明したこの記事はクラスとインスタンスの概念がつかみやすくていいですね。

よくある、車の設計図-車の例や、メニュー-料理の例よりもだいぶわかりやすいと思います。

まあドラクエを知っていることが前提なんですが・・・。

ルイーダの酒場には、きっとcharacter()の名前で定義したクラス、つまりプラスチック人形がいるんでしょう。

そして、勇者の希望に応じて、シールをペタペタ貼ってキャラクターを作って提供しているんでしょうね(笑)。

以上です。