オブジェクト指向のクラスとは

クラスを知る前にサブルーチンについて確認していきましょう。

サブルーチン(関数)とは

サブルーチン(関数)は処理をまとめたプログラムで、メインルーチンから呼び出されます。

リモコンから信号を発するイメージのプログラムを作ってみました。 この解説ではこのプログラムを軸に解説していきます。
通常であれば赤外線を送出するプログラムになりますが、簡易的にprintでリモコンから信号が出ているイラストにしました。
メインルーチンで、サブルーチンsignal_onに「00000000」の値を渡しています。
サブルーチンでは値を受け取り、リモコンのイラストを付けて出力します。

サブルーチンは何度も呼び出すことができ、呼び出す側であるメインルーチンが増えても、呼び出される側であるサブルーチンを修正する必要がありません

#サブルーチン
def signal_on(signal):
    print('【リモコン】<'+signal)

#メインルーチン
#電源を付ける信号を'00000000'とする
signal_on('00000000')

#【リモコン】<00000000

サブルーチンはC言語などでも使われていますし、比較的容易にイメージできると思います。 この時点でで分からないことがある場合は先に進まないほうが賢明です。
自分でプログラムを書いて理解を深めてみましょう。
次にいくつか単語が出てきますが、重要な点なのでしっかり理解しながら先に進みましょう。

抽象化と具体化

よく例えに出されるのが以下の例ですが、現実世界の例えはプログラムとして考えるときに混乱を招きやすいのでザックリ分かれば大丈夫です。

抽象度を上げることを抽象化、下げることを具体化と言います。
この例では左に行くほど抽象度が高いです。
「生物>哺乳類>犬>柴犬>ぽち」
「乗り物>車>消防車>車番」

具体化

具体化は分かりやすいです。
犬には沢山の犬種がありますし、哺乳類には犬以外にも猫や鯨など犬以上に種類があります。 ぽちを特定するには、「生物で、哺乳類で、犬で、柴犬の、ぽち」となります。
このように要素を絞り込んでいくことを具体化といいます。

抽象化

具体化の反対です。 「ぽちは柴犬に含まれ、犬に含まれ、哺乳類に含まれ、生物に含まれる」と、具体的な要素や概念を減らして範囲を広げていくことです。 犬と猫がいた場合そのままでは別々の生き物ですが、抽象化することで哺乳類という分類に入れることができます。
一般に子供や汎用コンピュータ*1は抽象化の能力が低いようです。
柴犬とチワワが並んでいても別の生き物だと認識し、犬という分類でとらえることができません。

クラスとは

まとめる

1つ目にサブルーチンや変数をまとめた分類のことです。

先ほどのプログラムにOFFのサブルーチンを追加します。
この規模では問題ありませんが、何十個と追加されたり、全く別の機能が追加されることがあります。
そうすると汚いプログラムになり*2、保守や仕様変更が大変になります。

#ONサブルーチン
def signal_on(signal):
        print('【リモコン】<ON:'+signal)
#OFFサブルーチン
def signal_off(signal):
        print('【リモコン】<OFF:'+signal)

#メインルーチン
signal_on('00000000')
signal_off('00000000')

#ON
#【リモコン】<ON:00000000
#OFF
#【リモコン】<OFF:00000000

そこで新たに信号系のsignalクラスを作りONとOFFをまとめることで、見やすくなったと思います。
sifnalクラスの中にon関数とoff関数があります。このようなクラス内の関数のことをメソッドと呼びます。 remo=remocon()インスタンス化、selfインスタンス化された自分自身を表しますが、詳細は後述します。

class remocon():
    #ON
    def on(self,signal):
        print('【リモコン】<ON:'+signal)
    #OFF
    def off(self,signal):
        print('【リモコン】<OFF:'+signal)

#メインルーチン
remo=remocon()
remo.on('00000000')
remo.off('11111111')

隠ぺいする

まとめることで綺麗に見える以外にもメリットがあります。
クラスをインスタンス化し、メソッドで外部とやり取りをすることで内部処理を気にすることなく処理を行うことができます。
またクラスで使われる変数には、クラス変数とインスタンス変数があります。 これらを使うことでカプセル化を行うことができます。

一気に単語が増えて頭の中がごちゃごちゃになってきたと思いますので、ひとつひとつ解説します。 とても重要なポイントなのでしっかり理解してください!

インスタンスとは

クラスを具現化したオブジェクト
難しいですね。

クラスの段階ではまだ概念だと思っていてください。
ぽちの例では犬や動物、リモコンの例ではON/OFFなどの信号をまとめたsignalがクラスになります。
「犬」や「信号」といった概念では動かすことができないので具現化します。
そのことをインスタンス化と言います。
先ほどのプログラムではこの部分がインスタンス化になります。

remocon=signal()

メリット

なぜわざわざインスタンス化する必要があるのか。
先ほどの具現化の話になりますが、クラスの段階ではまだ抽象的な概念です。
それをインスタンス化することで、実体のあるオブジェクトとして操作することできるようになります。
そして、インスタンスはいくつも作ることができます。

今回は「aircon」「fan」の2つのオブジェクトを作ります。

class remocon:
 #初期化
    def __init__(self,x):
  #インスタンス変数のmachineに代入
        self.machine = x
    #ON
    def on(self,signal):
        print('【'+self.machine+'】<ON:'+signal)
    #OFF
    def off(self,signal):
        print('【'+self.machine+'】<OFF:'+signal)

#2つインスタンス化
aircon = remocon("エアコン")
fan = remocon("扇風機")

#インスタンスごとに機器名が保持されている
aircon.on('00000000')
aircon.off('00000000')
fan.on('11111111')
fan.off('11111111')

#【エアコン】<ON:00000000
#【エアコン】<OFF:00000000
#【扇風機】<ON:11111111
#【扇風機】<OFF:11111111

解説すると、ここで「remoconクラス」の概念が「aircon」「fan」の2つのインスタンスになりました。

aircon = remocon("エアコン")
fan = remocon("扇風機")

次に渡した「エアコン」と「扇風機」の値を、「init」というメソッドで自身のインスタンス変数である「machine」に代入しました。 「init」は最初だけ実行される特別なメソッドになり、コンストラクトと呼ばれます。 ここでselfを使います。この変数はクラス内のどこからでも使うことができます。 更に詳細を知りたい方はこのブログを参考にすると良いでしょう。 python.ms

 #初期化
    def __init__(self,x):
  #インスタンス変数のmachineに代入
        self.machine = x

そのため同じon/offメソッドを使っても、インスタンスごとにカッコ内の機器名が異なるようになりました。

aircon.on('00000000')
aircon.off('00000000')
fan.on('11111111')
fan.off('11111111')

クラス変数とインスタンス変数

ではインスタンス変数とは何でしょうか。 クラスの中にはクラス変数とインスタンス変数の2種類あります。 ここを進む前に、にローカル変数とグローバル変数の知識があると良いでしょう。

クラス変数 インスタンス変数
定義する場所 classの直下 メソッドの直下
値は 全てのインスタンスで共有 インスタンスごとに独立
クラスオブジェクト
からの参照
インスタンスオブジェクト
からの参照
変更 検証を参照 検証を参照

定義する場所

classの直下にクラス変数を定義します。
クラス変数power=100を定義してみました。
インスタンス変数はself.machineという形式で定義します。

class signal:
    power =100 #クラス変数

    def __init__(self,x):
        self.machine = x #インスタンス変数

クラスオブジェクトからの参照

クラスオブジェクトからはクラス変数は参照可能です。
インスタンス変数は参照不可です。

class signal:
    power =100 #クラス変数

    def __init__(self,x):
        self.machine = x #インスタンス変数

fan = signal("扇風機")
print(signal.power)
#100 <--参照可能
print(signal.machine)
#AttributeError: type object 'signal' has no attribute 'machine' <--エラー参照不可

インスタンスオブジェクトからの参照

インスタンスオブジェクトからは、クラス変数、インスタンス変数ともに参照可能です。

class signal:
    power =100 #クラス変数

    def __init__(self,x):
        self.machine = x #インスタンス変数

fan = signal("扇風機")
print(fan.power)
#100 <--参照可能
print(fan.machine)
#扇風機 <--参照可能

値の変更

以下のプログラムの下に書いていく形で検証します。

class signal:
    power =100 #クラス変数

    def __init__(self,x):
        self.machine = x #インスタンス変数

fan = signal("扇風機")
aircon = signal("エアコン")
1.クラスオブジェクトからクラス変数を変更

変更可能かつ、全てのクラス変数が変更されました。

signal.power = 200
print(signal.power) 
print(fan.power)
print(aircon.power)

#200
#200
#200
2.インスタンスオブジェクトからクラス変数を変更

変更可能ですが、指定したインスタンスのクラス変数に限ります。*3

fan.power = 1000
print(signal.power)
print(fan.power)
print(aircon.power)
#100
#1000
#100
3.クラスオブジェクトからインスタンス変数を変更

先ほどはクラスオブジェクトからインスタンス変数は参照不可でしたが、代入することで参照できるようになりました。*4

signal.machine = "ライト"
print(signal.machine)
print(fan.machine)
print(aircon.machine)
#ライト
#扇風機
#エアコン
4.インスタンスオブジェクトからインスタンス変数を変更

変更可能ですが、指定したインスタンスインスタンス変数に限ります。

fan.machine= "扇風機改"
print(signal.machine)
print(fan.machine)
print(aircon.machine)
#AttributeError: type object 'signal' has no attribute 'machine' <--エラー
#扇風機改
#エアコン

このような特徴を用いることによりカプセル化が実現できます。

カプセル化(カプセルか、英: encapsulation)とは、データ(属性)とメソッド(手続き)を一つのオブジェクトにまとめ、その内容を隠蔽することを言う。

増やす

前項の「インスタンスとは」でインスタンス化をすることで、オブジェクトを増やすことができると学びました。(踏み込みすぎました・・・)
この仕組みはオブジェクト指向独特の機能です。

メモリ確保

初心者のときはメモリ確保については全く考えないでしょう。 しかしインスタンスとメモリ確保は密接な関係にあります。
なぜならインスタンスが生成された時点で、その分のメモリ領域が確保されます。
それにより同時に複数のインスタンスを処理することができるようになるのです。

まとめ

単語 意味
サブルーチン 関数。処理をまとめたもの
クラス サブルーチンや変数をまとめたもの
メソッド クラスにあるサブルーチン
インスタンス クラスという概念を、具体化して操作できるようにしたもの
インスタンス変数 self.(変数名)で定義
メソッドで宣言されてる
インスタンスごとに独立
クラス変数 クラスで宣言される
インスタンスに共通
カプセル化 クラスやメソッドを用いて処理を隠蔽すること

*1:それを補うために機械学習などがあります。

*2:スパゲッティコードといいます。

*3:イレギュラーな使い方でしょうか?

*4:こちらもイレギュラーな使い方でしょうか?