この記事を書くきっかけ
毎日、プログラミングの問題に挑戦している初心者です。日々コツコツと問題に取り組んでいたら、クラスの問題に出会うようになりました。やっと中級レベルの問題に挑戦できるようになって嬉しいのですが、これがなかなか難しいのです。
すでに完成されたクラスのコードを見るだけなら、何が書いてあるかを理解することはできるのですが、「一からクラスを書け!」、「継承もしろ!」、「その上で問題も解け!」というようなプログラミングの問題には正直大苦戦です。当然、初級の問題よりコードを書く分量が増えますし、途中で何を書いているのかわからなくなります。
なぜこんなに苦戦するのか考えてみると、自分がクラスに関係する言葉について、いまいち理解できていないことに気づきました。オブジェクトとインスタンスの違いもわかっていませんし、__init__やselfをちゃんと説明することもできません。
そこで、今回はクラスを書くときに必要な言葉や概念を、できるだけ(まずは自分が)理解できるようにまとめてみました。
クラスとは何か
クラスとは設計図のようなもの。
具体的に言うと、クラスは特定の種類の実体(オブジェクト)がどんな特性(データ)を持ち、どんな動きをするか(機能)を定義します。クラスを使うことで、共通の特性や動きを持つ実体(オブジェクト)を簡単にたくさん作ることができます。
インスタンスとは何か
インスタンスとは、クラスという設計図から作られた具体的な実体(オブジェクト)のことを指します。クラスが定義された後、そのクラスを基に作られた各オブジェクトがインスタンスです。
クラスとインスタンスの関係
クラス:設計図。どんな属性(データ)やメソッド(機能)を持つかを定義します。
インスタンス:設計図を基に作られた具体的なもの(オブジェクト)。クラスの属性やメソッドを実際に持ち、使用することができます。
クラスとインスタンスの関係を実際の家作りに例えると
設計図(クラス)
家を建てるための設計図には、部屋の配置、ドアの数、窓の位置などが詳細に書かれています。
家(インスタンス)
この設計図を基に建てられた具体的な家がインスタンスです。設計図からは何軒も家を建てることができますが、それぞれの家が個別のインスタンスとなります。
分譲住宅をイメージするといいです。同じ設計図をもとに建てられているため、それぞれの家の基本的な構造や特徴は同じですが、それぞれの家が個別に異なる住所や色などの特色を持つことができます。
オブジェクトとは何か
オブジェクトは「実体」です。クラスという設計図に基づいて作られた実際のデータです。例えば、「Student」というクラスがあれば、それを元に「taro」という名前の具体的な「人」をオブジェクトとして作ります。
オブジェクトとインスタンスの違い
オブジェクト:プログラム内で扱われる具体的なデータ単位。
インスタンス:特定のクラスから作られたオブジェクト。
オブジェクトは、プログラム内で扱うことのできるデータの単位であり、データとその操作を一つにまとめたものです。さまざまな種類があり、クラスから作られたものもあれば、基本的なデータ型(例えば整数や文字列)もオブジェクトと呼ばれます。実際のデータ全般を指します。
一方、インスタンスは、特定のクラスから生成されたオブジェクトを指します(特定のクラスの具体的な実体)
そもそもなぜクラスを使うのか
- クラスを使うと、同じコードを繰り返し使うことができるから。クラスを定義しておけば、そのクラスを何度もインスタンス化して使うことができます(再利用性)
- データとその操作をまとめて一つの構造として扱えるので、プログラムの構造が明確になり、コードを読む人が理解しやすくなります(構造化)
- データとそれに関連する操作をクラス内に隠すことができるから。データの不正な操作を防ぐことができます(カプセル化)
- 既存のクラスを基に新しいクラスを作ることができます。これにより、コードの重複を避け、効率的にプログラムを開発できます(継承)
とりあえず、なんか便利だから!という理解でいいと思います。
クラスの書き方
class Student: #クラスの宣言(大文字始まり)
def __init__(self, name, age): # コンストラクタ
self.name = name # メンバ変数
self.age = age
def profile(self): # メンバ関数
print(f"My name is {self.name}!")
def get_age(self):
return self.age
コンストラクタ(__init__メソッド)
コンストラクタとは、クラスのインスタンスが作られるときに自動的に呼ばれる特別なメソッドです。例では、__init__メソッドがコンストラクタに当たります。__init__メソッドの主な目的は、新しいインスタンスの初期化を行うことです。つまり、インスタンスが作られたときにその属性を設定したり、必要な初期設定を行います。
コンストラクタに限らず、クラス内で定義するメソッドの第1引数は「self」を指定します。「self」の役割はその関数を呼び出すインスタンス自身を引数として取得することです。
def __init__(self, name, age):
self.name = name
self.age = age
__init__メソッドは新しいインスタンスを初期化するコンストラクタです。selfはインスタンス自身を指し、nameとageはこのインスタンスの属性(メンバ変数)を指します。
メンバ変数(属性)
メンバ変数はクラスのインスタンスごとに持つデータです。例では、nameとageがメンバ変数です。
メンバ関数(メソッド)
メンバ関数はクラスのインスタンスが持つ関数です。例では、profileとget_ageがメンバ関数です。
インスタンス
インスタンスはクラスから作られた具体的なオブジェクトです。
Studentクラスを使ってインスタンスを作る方法
# クラス
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def profile(self):
print(f"My name is {self.name}!")
def get_age(self):
return self.age
# インスタンスの作成
student1 = Student("Alice", 20)
print(student1.name) # Alice
student1.profile() # My name is Alice!
print(student1.get_age()) # 20
1行目: student1 = Student("Alice", 20)
Studentクラスの新しいインスタンスを作成しています。
- Student("Alice", 20)の部分では、Studentクラスのコンストラクタである__init__メソッドが呼び出されます。
- __init__メソッドは、引数として渡されたnameとageを使って、インスタンスの属性を初期化します。具体的には、self.nameに"Alice"を、self.ageに20を設定します。
- 作成されたインスタンスはstudent1という変数に代入されます。これにより、student1はStudentクラスの具体的なインスタンスとなります。
2行目: print(student1.name)
student1インスタンスのname属性の値を取得して出力する
- student1.nameは"Alice"に設定されているため、この行はAliceと表示します。
3行目:student1.profile()
student1インスタンスのprofileメソッドを呼び出す
- profileメソッドは、インスタンスのname属性を使って自己紹介のメッセージを表示します。
- 具体的には、print(f"My name is {self.name}!")というコードが実行され、self.nameが"Alice"であるため、My name is Alice!と表示されます。
4行目: print(student1.get_age())
student1インスタンスのget_ageメソッドを呼び出し、その返り値を出力する。
- get_ageメソッドは、インスタンスのage属性の値を返すメソッドです。
- この場合、student1のage属性は20であるため、この行は20と表示されます。
まとめ
記事を書くことで自分の理解の助けになると思い、クラスについてまとめました。それでも、「クラスに関しては理解には時間がかかるな〜」と思っています。少しはクラスへの解像度が上がったはずなので、いろいろなコードを読んで、クラスへの抵抗感を減らしたいと思います。
詳細に知りたい方は公式ドキュメントを参考にしてみてください。