Pythonロボティクスコース レッスン 8 *

数値や文字列などオブジェクトの型を決める「クラス」について学ぼう

このレッスンで学ぶこと

テーマ.1-4では、Pythonのデータは全てオブジェクトであり、プロパティとメソッドを持つことを学習しました。オブジェクトのように、データを抽象化して扱うことで、プログラムが効率的に開発できます。また、その特徴が人の属性情報や振る舞いに例えられたように、現実の世界と似たルールを持つため、プログラムの構造が理解しやすいという利点がありました。

このレッスンでは、オブジェクトを扱うプログラミングについて、より理解を深めるために、オブジェクトの作成に必要な「型」について学習します。例えば、これまで扱ってきた整数や文字列のデータはそれぞれ整数型(int型)・文字列型(str型)のオブジェクトでした。pythonでは、この型のことを「クラス」と呼びます。クラスは整数や文字列のようにあらかじめpythonで用意されているものだけでなく、自分で新たに作成することもできます。

レッスンの最後では、Studuino:bitを制御するために用意されている、いくつかのクラスを使い、簡単なプログラムを作成します。実際にプログラムを作成しながらクラスについて理解を深めましょう。

※関数とは
https://www.sejuku.net/blog/40791

クラスとインスタンス

クラスは複雑な機能を取り払えば、「クッキーの型抜き器」と考えることができます。つまり、あらかじめクラスを定義してオブジェクトの形を決めておき、型抜き器のようにその型で抜くことで、その性質をもつオブジェクトが作成できるということです。こうして作成されたオブジェクトのことを「インスタンス」と呼びます。

作成されたインスタンスは同じ形でも1つ1つは違うオブジェクトです。また、クッキーで例えるなら、チョコチップを入れたり、小さなキャンディを入れたりするなど1つ1つに違う味付けができるのと同じように、それぞれのインスタンスに違う情報を持たせることもできます。

ここからは、独自のクラスを実際に作りながら、クラスとインスタンスの関係について確認していきます。

2. 1 クラスの定義

クラスを定義するときは「class」というキーワードを使います。これは、分岐処理の「if」や反復処理の「for」、関数の「def」と同じように、「 :(コロン)」の後にインデントを入れた部分がクラス定義の適用範囲になります。

class クラス名:
	クラスの定義(プロパティやメソッドなど)
	.
	.
	.

早速、例として次の犬のクラス「 Dog 」を定義してみましょう。

クラス名の付け方に特に決まりはありませんが、Pythonではインスタンスと区別するために1文字目を大文字にする慣習があります。そのため、dog ではなく Dog という名前でクラスを作ります。

では、インデントに気を付けながら、次のコードをエディタエリアに書き写しましょう。

【サンプルコード 2-1-1】
  1. class Dog:
  2.   voice = ‘Bow!’
  3.   def bark(self):
  4.     print(self.voice)

では、コードの中身を詳しくみていきます。

2行目では、クラスのプロパティを定義しています。プロパティは「voice = 'Bow!'」のように変数として定義します。

3行目~4行目では、メソッドを定義しています。メソッドは関数と同じでdef文で作成します。ただし、関数とは違い、メソッドの1番目の引数に必ず「self」を指定する必要があります。self はこのクラスで作成したインスタンス自身を指しており、「self.プロパティ名」や「self.メソッド名()」とすることで、メソッド内でインスタンスがもつプロパティや別のメソッドを利用することができます。実際に、bark()メソッドでは、5行目の「print(self.voice)」でプロパティ voice を利用しています。

self はメソッド内のコードで使わない場合でも必ず引数として指定する必要があります。もし、省略した場合はエラーが発生するので注意してください。また、省略はできませんが、self 以外の言葉を使うことはできます。しかし、慣習としてselfが使われていることもあり、特別に理由がなければ、self を使うようにしてください。

2. 2 インスタンスの作成

では、定義したDogクラスのインスタンスを作成しましょう。インスタンスは次のコードで作成できます。

インスタンス名 = クラス名()

ここでは、インスタンス名を「dog」として作成し、bark()メソッドを実行してみます。インデントがないことに気を付けて、【サンプルコード 2-1-1】に、以下の7行目~8行目のコードを追加しましょう。

【サンプルコード 2-2-1】
追加【7行目~8行目】
  1. class Dog:
  2.   voice = ‘Bow!’
  3.  
  4.   def bark(self):
  5.     print(self.voice)
  6. dog = Dog()
  7. dog.bark()

bark()メソッドの定義では1番目の引数に self が必要でしたが、実行する場合はこれを省略します。そのため、self 以外に別の引数がある場合は、2番目の引数から指定します。実際に、このプログラムを実行すると、ターミナルにBow!と表示されます。

Bow!

ここまでで、クラスの定義からインスタンスの作成とメソッドおよびプロパティの利用までを一通り確認しました。クラスにはこれ以外にも様々な性質があります。もう少し詳しく見ていきましょう。

2. 3 コンストラクタについて

さきほどのプログラムでは、鳴き声(voice)はすべての犬で共通しているものとして「Bow!」と定義しました。これとはちがい、名前(name)のように1匹1匹の犬ごとに異なる情報を持たせたい場合もあります。鳴き声のように、クラスに共通するプロパティを扱う変数を「クラスメンバ変数」、インスタンスごとに異なるプロパティを扱う変数を「インスタンスメンバ変数」といい、定義の仕方が異なります。

インスタンスメンバ変数として、プロパティを追加する方法はいくつかありますが、インスタンスを作る時点で分かっている情報はインスタンス作成時に行われる初期処理で追加するのが一般的です。この初期処理のことを「コンストラクタ」と呼びます。

コンストラクタは__init__という固定された名前のメソッドとして定義します。initの前後には「_(アンダースコア)」が2個ずつ必要です。では、実際に以下のように【サンプルコード 2-2-1】から書き換えて、名前(name)のプロパティをインスタンス作成時に追加してみましょう。

【サンプルコード 2-3-1】
追加・変更【4行目~5行目、10行目、12行目】
  1. class Dog:
  2.   voice = ‘Bow!’
  3.   def __init__(self, name):
  4.     self.name = name
  5.   def bark(self):
  6.     print(self.voice)
  7. dog = Dog(‘Taro’)
  8. dog.bark()
  9. print(dog.name)
(実行結果)

Bow! Taro

__init__()メソッド内の5行目「self.name = name」に注目してください。これで、インスタンス作成時に犬の名前が指定されると、インスタンスメンバ変数「name」にその名前を代入するようになっています。

また、self.name と引数の name は同じ名前ですが、異なるオブジェクトとして区別されています。このようにインスタンスメンバ変数とコンストラクタの引数を同じ名前にするのがPythonでは一般的ですが、これでは分かりにくいという場合は引数の名前を変えても構いません。

ちなみに、voiceのようなクラスメンバ変数にはインスタンスを作成しなくても直接利用できるというインスタンスメンバ変数にない特徴があります。そのため、「print(Dog.voice)」は実行できますが、「print(Dog.name)」はエラーが発生します。実際に確かめてみてください。

>>> print(Dog.voice)
Bow!
>>> print(Dog.name)
Traceback (most recent call last):
 File “<stdin>”, line 1, in <module&gt
AttributeError: type object ‘Dog’ has no attribute ‘name’
>>>

※エディタエリアに書いたプログラムを実行すると、その中で定義されたクラスや作成されたインスタンスをターミナルのコードでも使うことができます。

2. 4 クラスメソッドについて

クラスの変数に「クラスメンバ変数」と「インスタンスメンバ変数」があったように、メソッドにも「クラスメソッド」と「インスタンスメソッド」があります。ここでは、この2つのメソッドの違いを見ていきましょう。

まず、クラスメソッドはdef文の直前に「@classmethod」の記述が必要です。反対に「@classmethod」がないものは、すべてインスタンスメソッドとなります。

@classmethod
def クラスメソッド名():

クラスメソッドはインスタンスからも実行できますが、インスタンスを作成せずに「クラス名.クラスメソッド名()」と書いても実行できます。インスタンスメソッドの処理結果は実行したインスタンスにのみ反映されますが、クラスメソッドの処理結果はすべてのインスタンスに反映されます。例として、クラスメンバ変数「voice」を別の鳴き声に変えるクラスメソッド「change_voice()」を作成してみましょう。【サンプルコード 2-3-1】に以下の10行目~12行目のコードを追加します。

追加【10行目~12行目】
  1. class Dog:
  2.   voice = ‘Bow!’
  3.   def __init__(self, name):
  4.     self.name = name
  5.   def bark(self):
  6.     print(self.voice)
  7.   @classmethod
  8.   def change_voice(self, voice):
  9.     self.voice = voice
  10. dog = Dog(‘Taro’)
  11. dog.bark()
  12. print(dog.name)

change_voice()メソッドは、引数に新しい鳴き声を取ります。この引数で、クラスメンバ変数voiceの値を置き換えています。

次にメソッドの実行結果が比較できるように、作成するインスタンスを2つに増やします。以下のようにコードを書き換えましょう。

【サンプルコード 2-4-1】
追加・変更【14行目~21行目】
  1. class Dog:
  2.     voice = ‘Bow!’
  3.     def __init__(self, name):
  4.        self.name = name
  5.     def bark(self):
  6.        print(self.voice)
  7.     @classmethod
  8.     def change_voice(self, voice):
  9.        self.voice = voice
  10.  
  11. dog1 = Dog(‘Taro’)
  12. dog2 = Dog(‘Jiro’)
  13.  
  14. dog1.bark()
  15. dog2.bark()
  16. dog1.change_voice(‘Woof!’)
  17. dog1.bark()
  18. dog2.bark()

このプログラムを実行すると、以下の結果がターミナルに表示されます。dog1で実行したchange_voice()メソッドの結果がdog2にも反映されていることが分かります。

(実行結果)

Bow! Bow! Woof! Woof!

では、10行目の@classmethodを削除して、同じく実行した結果も確認しておきましょう。

【サンプルコード 2-4-2】
削除後【10行目】
  1. class Dog:
  2.     voice = ‘Bow!’
  3.     def __init__(self, name):
  4.         self.name = name
  5.     def bark(self):
  6.         print(self.voice)
  7.     def change_voice(self, voice):
  8.         self.voice = voice
  9.  
  10. dog1 = Dog(‘Taro’)
  11. dog2 = Dog(‘Jiro’)
  12.  
  13. dog1.bark()
  14. dog2.bark()
  15. dog1.change_voice(‘Woof!’)
  16. dog1.bark()
  17. dog2.bark()
(実行結果)

Bow! Bow! Woof! Bow!

change_voice()メソッドは、インスタンスメソッドとなっているため、呼び出し元のdog1にだけ、結果が反映されています。

インスタンスメソッドとクラスメソッドにはこのような違いがありますが、クラスメソッドを使うと全てのインスタンスに影響があるため、予期しない使われ方をするとエラーを招く恐れもあります。そのため、基本はインスタンスメソッドを利用して、それでは実現できないことがある場合にクラスメソッドの利用を検討するようにしましょう。

別ファイルからの機能の取り込み

上で学習したことから、テーマ.2-2でも書いた以下のコードの意味が理解できるようになります。

from pystubit.board import Image

img = Image('11111:11111:11111:11111:11111:')

Studuino:bitにはあらかじめ、LEDディスプレイに表示するイメージを作成するための「Imageクラス(正式にはStuduinoBitImageクラス)」が用意されています。3行目のコードはImageクラスのインスタンスを作成するためのもので、引数の'11111:11111:11111:11111:11111:'は、Imageクラスのコンストラクタである__init__()メソッドの引数として指定しています。

また、1行目に書いているfrom pystubit.board import Imageでは、別のファイルで定義されているImageクラスを現在のファイルに取り込むという処理を行っています。

fromは日本語で「~から」という意味で、import (インポート)
は「取り込む」という意味があります。つまり、「from pystubit.board import Image」は「pystubit.boardからImageクラスを取り込む」ことを行っています。このfrom-import文では、クラスだけでなく、変数や関数も取り込むことができます。

from 参照元のファイルまでの場所 import 取り込む機能(クラス、変数、関数)

1つのファイルから複数の機能を取り込む場合は「,(カンマ)」で区切って書き連ねます。

from 参照元のファイルの場所 import 機能1, 機能2, 機能3, ...

これまでも何度か出てきた以下のコードでは、既に作成されたオブジェクト(StuduinoBitDisplayクラスのインスタンス)を取り込んでいました。

from pystubit.board import display

その他にもStuduino:bitでは機能ごとに専用のクラスが定義されており、pystubit.boardでは、イメージのクラスを除き、それぞれのクラスのインスタンスが用意されています。

機能クラス名pystubit.boardで用意されている</br>インスタンス名
ボタンStuduinoBitButtonbutton_a(ボタンA),button_b(ボタンB)
ブザーStuduinoBitBuzzerbuzzer
LEDディスプレイStuduinoBitDisplaydisplay
イメージStuduinoBitImageなし※代わりにImageという省略したクラス名が定義されています。
温度センサーStuduinoBitTemperaturetemperature
光センサーStuduinoBitLightSensorlightsensor
加速度センサーStuduinoBitAccelerometeraccelerometer
ジャイロセンサーStuduinoBitGyrogyro
コンパス(磁気センサー)StuduinoBitCompasscompass

また、次のように import に続けて、「*(アスタリスク)」を書くと、参照元のファイルからすべての機能を取り込むことができます。

from pystubit.board import *

上のコードはStuduino:bitの複数の機能を使うときに大変便利な書き方です。もちろん、必要な機能のクラスやインスタンスを1つずつ取り込んでも構いません。

これから先、複雑なプログラムを書いていくと、どうしてもプログラム全体が長くなり、どこにどのような処理が書かれているのかが分かりにくくなってきます。そのような場合は、機能ごとにプログラムを分割し、必要に応じてfrom-import文で取り込むようにするのです。

課題:LEDディスプレイに現在の温度が表示されるプログラムの作成

Studuino:bitの温度センサーから現在のStuduino:bit内部の温度を取得して、LEDディスプレイにスクロール表示するプログラムを作成してみましょう。

4. 1 プログラムの作成

ここでは別ファイルから直接インスタンスを読み込むのではなく、クラスを読み込んでからインスタンスを作成します。各機能のクラスはそれぞれ次の場所にあるファイルで定義されています。

機能クラス名参照元のファイルの場所
LEDディスプレイStuduinoBitDisplaypystubit.dsply
温度センサーStuduinoBitTemperaturepystubit.sensor

そのため、各クラスは次のコードで取り込むことができます。

  1. from pystubit.dsply import StuduinoBitDisplay
  2. from pystubit.sensor import StuduinoBitTemperature

次に、各クラスのインスタンスを作成します。どちらもインスタンスを作成するときに、引数は必要ありません。以下のコードを追加しましょう。

追加【4行目~5行目】
  1. from pystubit.dsply import StuduinoBitDisplay
  2. from pystubit.sensor import StuduinoBitTemperature
  3.  
  4. display = StuduinoBitDisplay()
  5. temperature = StuduinoBitTemperature()

最後に、温度センサーから現在の温度を取得して、LEDディスプレイにスクロール表示します。温度の取得はStuduinoBitTemperatureクラスのget_celsius()メソッドで行います。このメソッドで取得した温度の単位は「℃」で、戻り値は数値型となっています。これをStuduinoBitDisplayクラスのscroll()メソッドでLEDディスプレイに表示しますが、そのため、str()関数で先に文字列に変換して渡します。

【サンプルコード 3-1-1】
追加【7行目~8行目】
  1. from pystubit.dsply import StuduinoBitDisplay
  2. from pystubit.sensor import StuduinoBitTemperature
  3.  
  4. display = StuduinoBitDisplay()
  5. temperature = StuduinoBitTemperature()
  6.  
  7. value = temperature.get_celsius()
  8. display.scroll(str(value))

4. 2 プログラムの実行

完成したプログラム【サンプルコード 3-1-1】を実行して動作を確認してみましょう。

小数点第2位までで表された温度の数字がLEDディスプレイに流れますが、この温度は気温ではなく、Studuino:Bit内部の温度となっていることに注意してください。

おわりに

5. 1 このレッスンのまとめ

このレッスンでは、オブジェクトを作成するために必要な「クラス」について学びました。ここでは紹介しきれませんでしたが、クラスには他にもオブジェクト指向プログラミングを実現するための機能がいくつかあります。これらについては入門編以降で詳しく説明していきます。まずは、ここで紹介した機能をしっかりと理解することに努めましょう。

5. 2 次回のレッスンについて

Pythonプログラミングの基礎の学習はこのレッスンで終わりです。次のレッスンでは、ここまでで学習したことを踏まえて、Studuino:bitのLEDディスプレイをPythonで制御して様々な作品を制作する実践的な課題に取り組みます。

TOP