こんにちは、かつコーチです。
Pythonの学習を進めていくと、必ず出てくるのがクラスという概念です。
「classって何のために使うの?」
「selfって何を指しているの?」
このあたりで挫折しかけた経験がある方も、少なくないのではないでしょうか。
この記事では、クラスとオブジェクト指向の基本を、初心者向けに一つずつ丁寧に解説します。
読み終える頃には、クラスを定義してインスタンスを作り、実際に動かせるようになります。
クラスとは?
設計図と実体という考え方
クラスとは、モノの性質や振る舞いをまとめた「設計図」のようなものです。
例えば「犬」というクラスがあれば、「名前」や「鳴く」という性質・動作を定義できます。
そして、その設計図から実際に作られた個々の「モノ」をインスタンス(オブジェクト)と呼びます。
「ポチ」や「タロウ」といった具体的な犬が、インスタンスにあたります。
なぜオブジェクト指向が必要なのか
関数だけでプログラムを書いていくと、データと処理があちこちに散らばってしまいます。
オブジェクト指向とは、データ(性質)と処理(振る舞い)を1つのまとまりとして扱う考え方です。
クラスを使うことで、関連する情報と処理をひとまとめにでき、コードの見通しがよくなります。
大規模なプログラムになるほど、この整理のしやすさがメリットとして効いてきます。
classでのクラス定義
最小限のクラスを作る
Pythonではclassキーワードを使ってクラスを定義します。
class Dog:
pass
my_dog = Dog()
print(my_dog)
実行結果は次の通りです。
<__main__.Dog object at 0x1041a3d90>
Dog()と書くだけで、Dogクラスからインスタンスが1つ作られます。
passは「何もしない」ことを表すキーワードで、中身が空のクラスを一時的に作るときに使います。
init(コンストラクタ)で初期値を設定する
クラスにはたいてい、インスタンスを作った瞬間に実行される特別なメソッドを用意します。
これが__init__(コンストラクタ)です。
インスタンスが作られるタイミングで、名前や年齢などの初期値を設定するのに使います。
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
my_dog = Dog("ポチ", 3)
print(my_dog.name)
print(my_dog.age)
実行結果は次の通りです。
ポチ
3
Dog("ポチ", 3)のように書くと、内部で自動的に__init__が呼ばれ、nameとageが設定されます。
selfの意味を理解する
__init__の第一引数に必ず登場するselfは、「作られたインスタンス自身」を指します。
self.name = nameは、「このインスタンス自身のnameに、渡された値を入れる」という意味です。
selfは予約語ではなく単なる慣習的な名前ですが、Pythonコミュニティでは必ずselfと書くのが一般的です。
メソッドを定義するときも、第一引数には必ずselfが必要になります。
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print(f"{self.name}: ワン!")
my_dog = Dog("ポチ", 3)
my_dog.bark()
実行結果は次の通りです。
ポチ: ワン!
barkメソッドの中でself.nameと書くことで、そのインスタンスが持つnameを参照しています。
インスタンス変数を理解する
インスタンスごとに独立したデータを持つ
self.nameやself.ageのように、selfに紐づけた変数をインスタンス変数と呼びます。
インスタンス変数は、インスタンスごとに独立した値を持てるのが特徴です。
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
dog1 = Dog("ポチ", 3)
dog2 = Dog("タロウ", 5)
print(dog1.name, dog1.age)
print(dog2.name, dog2.age)
実行結果は次の通りです。
ポチ 3
タロウ 5
同じDogクラスから作られていても、dog1とdog2はそれぞれ別のname・ageを持っています。
複数のメソッドを組み合わせる
クラスには、__init__以外にも自由にメソッドを追加できます。
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print(f"{self.name}: ワン!")
def birthday(self):
self.age += 1
print(f"{self.name}は{self.age}歳になりました")
my_dog = Dog("ポチ", 3)
my_dog.bark()
my_dog.birthday()
実行結果は次の通りです。
ポチ: ワン!
ポチは4歳になりました
birthdayメソッドの中でself.ageを書き換えることで、インスタンス変数の状態を更新できます。
よくあるつまずきポイント・エラー対処
selfを書き忘れてエラーになる
筆者が初心者の頃、最も苦しんだのがselfの書き忘れによるエラーです。
メソッドを定義するとき、第一引数のselfをつい省略してしまいがちです。
❌ Before(エラーになる書き方)
class Dog:
def __init__(name, age):
name = name
age = age
my_dog = Dog("ポチ", 3)
実行すると、次のエラーメッセージが表示されます。
TypeError: Dog.__init__() takes 2 positional arguments but 3 were given
一見「引数が足りない」ようなメッセージですが、実際にはselfが抜けているため、渡した"ポチ"がnameの位置にずれて入り、引数の数が合わなくなっているのが原因です。
✅ After(正しい書き方)
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
my_dog = Dog("ポチ", 3)
print(my_dog.name)
メソッドの第一引数には必ずselfを書き、self.nameのように紐づけることで、インスタンス変数として正しく保存されます。
このエラーメッセージを見たら、まずselfの書き忘れを疑うと解決が早いです。
まとめ
この記事のポイント
- クラスは設計図、インスタンスはそこから作られた実体である
classキーワードでクラスを定義する__init__はインスタンス作成時に自動で呼ばれるコンストラクタであるselfは「そのインスタンス自身」を指し、メソッドの第一引数に必須であるself.変数名で定義した値をインスタンス変数と呼び、インスタンスごとに独立している
次に読むべき記事
- 継承とオーバーライドの書き方
- デコレータの基本:関数を拡張する仕組み
- プロパティ(@property)でgetter/setterを実装する
タグ: Python, 初心者向け, 基本文法