いわゆるオブジェクト指向言語の学習で壁となることが多いクラスのお話です。
これがわからないとプログラムを書けないというわけではありませんが、知っていると便利になることも多いです。まずは、こんなものがあるんだな~くらいの感覚で見ていただければと思います。
クラスとはオブジェクトの設計図となるもので…という説明がありますが、これもちょっとわかりにくいので、正確さはさておき、もう少しおおざっぱに見ていこうと思います。
まず、簡単に言ってしまえばクラスとは、
です。
では早速、クラスを使ってみましょう。
ここでは、図形の情報を管理することを考えてみます。例えば四角形を表すためには、縦横の長さが必要です。これらを持つクラスを定義してみましょう。
class Rectangle:
def __init__(self):
self.tate = 2
self.yoko = 3
リスト:クラスRectangleの定義
図:クラスの内容
いま、2つの変数tateとyokoを持つクラスRectangleを作りました。こうすることで、2つの変数を1つのかたまりとして扱うことができます。
クラスRectangleのオブジェクトを作るには、クラス名にカッコをつけた形で呼び出します。
なお、クラスのオブジェクトのことをインスタンスといい、インスタンス化とも言います。あるいは、クラスという設計図から実際に使える変数を作ることから、実体化などとも言います。
ちなみに、インスタンス化とかオブジェクトの作成と言っても、難しく考えることはありません。これまでも、同じようなことをやってきています。
例えば、文字列型の変数を作るときに、
a = "hello"
などと作ることもありますが、
a = str("hello")
として作ることもあります。この書き方こそ、クラスのオブジェクトを作成しているのに他なりません。
さて、話を戻しましょう。
クラスの定義では、多くの場合 __init__() という特別な名前のメソッドも一緒に定義します。メソッドとは、ここでは関数と同じようなもので、処理のかたまりに名前をつけたものと思って下さい。
この__init__()は、クラスオブジェクトが作られたときに、自動的に実行されるメソッドです。このなかで、頭に self. をつけた変数を作ることで、クラスのオブジェクトに紐付けられた変数を作ることができます。この変数は、オブジェクト毎に固有の値を持ちます。言い換えると、Rectangleクラスから生成したオブジェクトそれぞれについて、異なる値を持つと言うことです。クラスから生成したオブジェクトのことをインスタンスと言うことから、こうした変数のことをインスタンス変数と呼びます。
ここまでを踏まえて、クラスのオブジェクトを作ってみます。
r = Rectangle()
print(r.tate)
print(r.yoko)
リスト: クラスオブジェクトの作成
変数rには、クラスRectangleを実体化したオブジェクトが入っています。
さらに、ドット(.)を使って、作成したオブジェクトに含まれる変数にアクセスしています。
このように、クラスのオブジェクトとそこに含まれる変数といったように、変数を階層的に管理することができます。
もう少し使ってみましょう。
今度はr1とr2として、2つのオブジェクトを生成してみます。さらに、それぞれのオブジェクトが持つ変数に値を設定してみましょう。
r1 = Rectangle()
r2 = Rectangle()
r1.tate = 10
r1.yoko = 20
r2.tate = 6
r2.yoko = 5
四角形の縦と横があると面積を求めたくなりますね。
print(r1.tate * r1.yoko)
print(r2.tate * r2.yoko)
ここまでで、クラスを使った例を見てきました。
「こういうことができる」というのはわかりましたが、これが便利なのか、あるいはこれをするメリットは何かというと、ちょっとわかりにくいと思います。これだけであれば、クラスを使わなくても個別に変数を4つ使えばよいようにも思えます。
クラスを使う利点は何なのか、引き続き、クラスを使った例を見てみましょう。
クラスRectangleには、tateとyokoという2つの変数があります。
これらは四角形の縦と横を意味していますが、今はオブジェクトを生成したときに固定値が設定されています。
先の例では、これらの変数にあとから値を設定しましたが、どうせならオブジェクトを生成したときに一緒に値が設定できるとよいですね。そんなときは、__init__()に引数を追加します。
それでは、クラスオブジェクトの生成時に、任意の値を指定できるようにしてみましょう。
class Rectangle:
def __init__(self, tate, yoko):
self.tate = tate
self.yoko = yoko
r1 = Rectangle(6,4)
print(r1.tate)
print(r1.yoko)
リスト: 初期値付きのRectangleオブジェクトの生成
このように、__init__()に引数を追加することで、オブジェクトを生成するときにインスタンス変数に値を渡して初期化することができます。
クラスRectangleには前述のように縦横を示すインスタンス変数がありますが、この変数を用いて面積を求めてみましょう。
先に紹介した例では、以下のようにクラスを使う側で直接演算をしていました。
r1 = Rentangle(5, 6)
r2 = Rentangle(3, 12)
print(r1.tate * r1.yoko)
print(r2.tate * r2.yoko)
リスト: 2つの四角形の面積(直接計算してみた)
四角形の面積なら縦横のかけ算なので、これでもよいですが、もっと複雑な計算になったら、何回も同じ処理を繰り返して書くのはちょっと非効率的ですね。そんな場合は、関数にして処理を使い回せばよいのでしたね。
def Area(tate, yoko):
return tate * yoko
r1 = Rentangle(5, 6)
r2 = Rentangle(3, 12)
print(Area(r1.tate, r1.yoko))
print(Area(r2.tate, r2.yoko))
リスト: 2つの四角形の面積(関数にまとめてみた)
関数化することで、ちょっとまとめることはできました。
さらに一歩進めて、この関数AreaをRectangleクラスのオブジェクト専用にしてしまう方法があります。
class Rectangle:
def __init__(self, tate, yoko):
self.tate = tate
self.yoko = yoko
def Area(self):
return self.tate * self.yoko
r1 = Rectangle(5, 6)
r2 = Rectangle(3, 12)
print(r1.Area())
print(r2.Area())
図:クラスの内容
これがクラスメソッドです。つまり、そのクラス専用の関数という意味です。
こうすることで、クラスが持つデータと、そのデータを使った処理をまとめて記述することができるため、効率よく見やすい実装ができるとされています。
今回は簡単な事例なのであまり効果が実感しにくいところですが、もっと複雑かつ多くのデータを持ち処理を行うクラスでは、見やすや管理のしやすさが違ってきます。
さて、ここまでクラスを実装する簡単な例を見てきました。クラスにするメリットはいろいろありますが、例えば次のようなものが挙げられます。
これは、今まで見てきたとおりですね。
例えばクラスの内部処理だけで使う変数やメソッドの内部だけの修正など、クラスを使う人には影響のない改造を行う場合、クラスにしておくことで、クラス内部の修正だけで済みます。もしこれがクラスでなかった場合、その処理を実行する場所すべてに修正が必要になってしまいます。
プログラミングをしていると、よく使う機能というのがあります。当然ながら公式で用意されているライブラリを使うことが大多数ですが、自分で作った便利な機能をよく使うこともあります。こうしたものをクラスにしておくと、あとからそのファイルをimporして使うことができるので便利です。
クラスの話は少しややこしいですが、実際に使ってみるとその便利さがだんだんとわかってくると思います。
さらに、クラスの話につきもの話で「派生」というものがあるのですが、これはまた別途にしたいと思います。
- クラスとは、大雑把に言うと、変数とそれらに対する処理をひとまとめにしたもの。
- 使うときには、クラスのオブジェクトを生成して使う。
- オブジェクト生成時に値を渡して初期値付きのオブジェクトを作ることもできる
- クラスの変数に対する専用の処理を書いた関数をメソッドと呼ぶ
コメント