閉じる

【Python】classの使い方入門|オブジェクト指向がスッとわかる解説

Pythonのプログラムを書いていると、変数や関数だけではコードがだんだんと複雑になっていきます。

そこで登場するのがclass(クラス)です。

classを使うと、データと処理をひとまとめに管理でき、現実世界の「もの」のイメージに近い構造でプログラムを組み立てられます。

この記事では、Python初心者の方でもclassの基本からオブジェクト指向の考え方までスッと理解できるよう、図解とサンプルコードを交えながら丁寧に解説します。

Pythonのclassとは何かを理解しよう

classの基本概念

Pythonにおけるclass(クラス)は、ひと言でいうと「オブジェクト(もの)を作るための設計図」です。

クラスそのものは「どんな情報を持ち、どんな振る舞いをするか」を定義した型のような存在で、そこから実際の「もの」であるインスタンスを作ります。

クラスには、大きく次の2つの要素があります。

1つ目は属性です。

これは、そのクラスが表す「もの」が持つデータのことです。

ユーザーなら名前や年齢、商品なら価格や在庫数などが属性にあたります。

2つ目はメソッドです。

これは、そのクラスが表す「もの」が行う処理や振る舞いのことです。

ユーザーなら自己紹介する、年齢を1つ増やす、商品なら在庫を減らす、価格を変更するといった処理がメソッドにあたります。

このようにして、1つのクラスの中に「データ」と「処理」をセットでまとめることで、コードの構造をわかりやすく整理できるのがclassの大きな特徴です。

オブジェクト指向とclassの関係

Pythonはオブジェクト指向プログラミングをサポートしている言語です。

オブジェクト指向とは、現実世界の「もの」や「概念」をプログラムの中でオブジェクトとして表現し、それらの関係性でシステムを組み立てる考え方です。

このオブジェクト指向の中心にある仕組みがclassです。

クラスを使うことで、次のようなオブジェクト指向の考え方を実現しやすくなります。

まずカプセル化です。

これは、データとそれを扱う処理をひとまとめにし、必要な窓口だけを外側に公開するという考え方です。

Pythonのクラスでは、属性とメソッドを1つのまとまりにし、アクセスのしやすさや隠蔽のレベルを工夫することで、カプセル化に近い設計ができます。

次に継承です。

あるクラスの機能を引き継ぎつつ、追加や変更を行える仕組みです。

親クラスに共通の処理を書き、子クラスで差分だけを記述することで、コードの再利用性を高められます。

最後にポリモーフィズム(多態性)です。

これは、同じ名前のメソッドでも、クラスごとに違った振る舞いをさせられるという性質です。

継承と組み合わせることで、柔軟な拡張が可能になります。

このように、classはオブジェクト指向の考え方をPythonで実現するための中核的な仕組みといえます。

関数との違い・モジュールとの違い

Pythonには、処理をまとめる仕組みとして関数モジュールもあります。

classと何が違うのかを整理しておきます。

関数は「1つの処理のまとまり」を表します。

入力(引数)を受け取り、結果(戻り値)を返す構造が基本で、データそのものを「状態」として長く持つことは想定していません。

あくまで「計算して返す」「処理して終わる」という役割です。

これに対してクラスは、「状態(属性)を持ち続け、その状態に基づいてさまざまな処理(メソッド)を行う」という点が大きく異なります。

例えば「ユーザー」というクラスを作れば、そのインスタンスは名前や年齢といったデータを保持しつつ、「自己紹介する」「年齢を更新する」などのメソッドを通じて継続的に操作できます。

一方、モジュールは「Pythonファイル単位でのまとまり」です。

モジュールの中には、クラス・関数・定数などを自由に定義できます。

つまり、モジュールはファイルという単位、クラスは型(設計図)という単位、関数は処理の単位と考えると整理しやすくなります。

classの基本構文と書き方

class定義の基本構文とインデント

Pythonでクラスを定義する基本的な構文は次のようになります。

Python
class クラス名:
    # ここにクラスの中身(属性やメソッド)を書く
    ...

Pythonではインデント(字下げ)がとても重要です。

クラス定義の行の下で、半角スペース4つ分などでインデントされたブロックが、そのクラスに属するコードになります。

具体的な例を見てみます。

Python
class User:
    # クラスの中身はインデントして書く

    def say_hello(self):
        # ここもクラスの中なので、さらにインデント
        print("こんにちは!")

このように、classの行に続くインデントされた範囲が、そのクラスの定義全体です。

インデントがずれると、思わぬ場所でクラスの外になってしまうため、特に注意が必要です。

クラス名の付け方

Pythonでは、クラス名にはCapWords方式(PascalCase)を使うのが慣習です。

これは、単語の先頭を大文字にし、単語同士をつなげて書く形です。

例えば、次のような名前がよく使われます。

  • User
  • ProductItem
  • BankAccount

逆に、変数名や関数名で使われるuserproduct_itemといったスネークケースは、クラス名には基本的に使いません。

これはクラスと関数・変数を見分けやすくするための慣習です。

また、クラス名は、そのクラスが表す「もの」や「概念」が何かを端的に表す必要があります。

例えば、ユーザーを表すならUser、銀行口座ならBankAccountというように、1〜2語で意味が伝わる名前が望ましいです。

インスタンス生成とコンストラクタ

クラスは設計図なので、そのままでは使えません。

実際に利用するにはインスタンス(オブジェクト)を生成します。

インスタンス生成の基本的な書き方は次のとおりです。

Python
変数名 = クラス名(引数...)

このときに呼ばれる特別なメソッドがコンストラクタと呼ばれる__init__メソッドです。

__init__はインスタンス生成時に1度だけ自動的に実行され、初期化処理を担当します。

簡単な例を見てみます。

Python
class User:
    # コンストラクタ(インスタンス生成時に呼ばれる)
    def __init__(self, name):
        # インスタンスが持つ「name」という属性を設定
        self.name = name

    def say_hello(self):
        print(f"こんにちは、{self.name}です!")


# Userクラスからインスタンスを生成
user1 = User("太郎")
user2 = User("花子")

user1.say_hello()
user2.say_hello()
実行結果
こんにちは、太郎です!
こんにちは、花子です!

この例では、User("太郎")と書いたときに__init__(self, name)が呼ばれ、nameという引数に"太郎"が渡されています。

インスタンス生成の際に必要な情報は、コンストラクタの引数として受け取り、インスタンスの属性に保存するというパターンをよく使います。

属性とメソッド

クラスには、インスタンスが持つ属性(データ)メソッド(処理)を定義します。

Pythonでは、どちらもself.名前という形で扱う点がポイントです。

サンプルコードで確認してみます。

Python
class User:
    def __init__(self, name, age):
        # インスタンス属性(インスタンスごとに異なる値を持つ)
        self.name = name
        self.age = age

    # メソッド(インスタンスの振る舞い)
    def introduce(self):
        print(f"私は{self.name}、{self.age}歳です。")

    def have_birthday(self):
        # 年齢を1つ増やす
        self.age += 1
        print(f"{self.name}は誕生日を迎えました!今は{self.age}歳です。")


user = User("太郎", 20)
user.introduce()
user.have_birthday()
user.introduce()
実行結果
私は太郎、20歳です。
太郎は誕生日を迎えました!今は21歳です。
私は太郎、21歳です。

この例からわかるように、属性はインスタンスの状態を表し、メソッドはその状態を読んだり書き換えたりするための操作です。

属性に直接アクセスすることもできますが、意味のある操作単位としてメソッドを用意しておくと、クラスの外側から扱いやすくなります。

selfの意味と使い方

Pythonのクラスを学ぶときに、多くの人が最初につまずくのがselfです。

selfは特別なキーワードではなく、「メソッドが呼ばれたときのインスタンス自身」を表す引数です。

インスタンスメソッドは、定義するときに必ず第1引数としてselfを受け取るように書きます。

Python
class User:
    def __init__(self, name):
        self.name = name

    # 第1引数にselfを書くのが約束事
    def say_hello(self):
        print(f"こんにちは、{self.name}です!")

インスタンス側からメソッドを呼ぶときにはuser.say_hello()のようにselfを明示的に渡していませんが、Pythonが内部的にsay_hello(self=user)のような形で呼び出してくれます。

つまり、selfは「いまどのインスタンスのメソッドとして動いているのか」を表す印であり、selfを通じてself.nameself.ageといったインスタンス固有のデータにアクセスしています。

オブジェクト指向の考え方をclassで学ぶ

カプセル化

カプセル化とは、データとそれを扱う処理を1つのまとまり(カプセル)に閉じ込め、外部からは必要な操作だけを提供するという考え方です。

Pythonのクラスは、このカプセル化を自然に実現しやすい仕組みになっています。

Pythonでは厳密なアクセス制御はありませんが、_nameのように先頭にアンダースコアを付けることで、その属性やメソッドを「内部用」として扱う目印にする慣習があります。

外側からは、公開されているメソッドを通じてデータを操作するように設計することで、クラスの内部構造を自由に変更しやすくなります。

メソッドでクラスの振る舞いを定義する

クラスは単なるデータ入れ物ではなく、メソッドによって「何ができるクラスなのか」を決める存在です。

振る舞いをメソッドとして設計していくことで、コードの意図が明確になります。

銀行口座の例を考えてみます。

Python
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner      # 口座名義人
        self.balance = balance  # 残高

    def deposit(self, amount):
        """入金処理"""
        if amount <= 0:
            print("入金額は正の数でなければなりません。")
            return
        self.balance += amount
        print(f"{amount}円入金しました。残高は{self.balance}円です。")

    def withdraw(self, amount):
        """出金処理"""
        if amount <= 0:
            print("出金額は正の数でなければなりません。")
            return
        if amount > self.balance:
            print("残高不足です。")
            return
        self.balance -= amount
        print(f"{amount}円出金しました。残高は{self.balance}円です。")


account = BankAccount("太郎", 1000)
account.deposit(500)
account.withdraw(300)
account.withdraw(1500)
実行結果
500円入金しました。残高は1500円です。
300円出金しました。残高は1200円です。
残高不足です。

このクラスを使う側は、depositwithdrawというメソッドだけを知っていればよく、残高のチェックやエラーメッセージの扱いなど内部の細かい処理は意識せずに済みます。

クラスの利用者にとって意味のある操作単位をメソッドとして設計することが、わかりやすいクラスを作るポイントです。

クラスメソッド・スタティックメソッドの違い

Pythonのクラスには、主に3種類のメソッドがあります。

1つ目はインスタンスメソッドです。

ここまで説明してきたselfを第1引数に持つメソッドがこれにあたります。

インスタンス固有のデータ(属性)を扱うのに使います。

2つ目はクラスメソッドです。

@classmethodデコレータを付け、第1引数にclsを取ります。

clsは「そのメソッドが属するクラス自体」を表します。

インスタンスではなく、クラス全体に関わる処理に使います。

3つ目はスタティックメソッドです。

@staticmethodデコレータを付け、selfもclsも受け取りません。

クラスに関係するけれど、インスタンスやクラスの状態には依存しないユーティリティ的な処理をまとめるのに使います。

具体例を見てみます。

Python
class Temperature:
    def __init__(self, celsius):
        # インスタンスごとの摂氏温度
        self.celsius = celsius

    def to_fahrenheit(self):
        """インスタンスメソッド: 自分自身の温度を華氏に変換"""
        return self.celsius * 9 / 5 + 32

    @classmethod
    def from_fahrenheit(cls, fahrenheit):
        """クラスメソッド: 華氏からインスタンスを生成"""
        celsius = (fahrenheit - 32) * 5 / 9
        return cls(celsius)

    @staticmethod
    def is_valid_celsius(celsius):
        """スタティックメソッド: 単純なチェック処理"""
        return -273.15 <= celsius  # 絶対零度未満でないか


t1 = Temperature(25)
print(t1.to_fahrenheit())  # インスタンスメソッド

t2 = Temperature.from_fahrenheit(77)  # クラスメソッド経由で生成
print(t2.celsius)

print(Temperature.is_valid_celsius(-300))  # スタティックメソッド
実行結果
77.0
25.0
False

このように、インスタンスメソッドは「個々のオブジェクトの振る舞い」、クラスメソッドは「クラス全体の工場・設定」、スタティックメソッドは「クラスに関連する単独の処理」というイメージで使い分けると理解しやすくなります。

__str__と__repr__でオブジェクトを見やすくする

クラスを使っていると、デバッグやログ出力のときにインスタンスの中身をわかりやすく表示したい場面がよくあります。

そのときに役立つのが__str____repr__というメソッドです。

__str__人間が読むためのわかりやすい文字列表現を返すメソッドで、print(obj)などで使われます。

一方__repr__開発者向けの、できれば「そのオブジェクトを再生成できるような」詳細な文字列表現を返すのが理想とされています。

Pythonのインタラクティブシェルでオブジェクトを入力したときなどには__repr__が使われます。

サンプルコードで確認してみます。

Python
class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        # ユーザー向けの見やすい表示
        return f"{self.name}({self.age}歳)"

    def __repr__(self):
        # 開発者向けの詳細な表示(再現しやすい形が理想)
        return f"User(name={self.name!r}, age={self.age!r})"


user = User("太郎", 20)

print(user)          # __str__ が呼ばれる
print(repr(user))    # __repr__ が呼ばれる
実行結果
太郎(20歳)
User(name='太郎', age=20)

このように__str__や__repr__を実装しておくと、デバッグやログ出力がとても見やすくなり、開発効率が上がります

classを一歩進んで活用する

継承の基本と使いどころ

継承は、あるクラス(親クラス / 基底クラス)の機能を引き継いで、新しいクラス(子クラス / 派生クラス)を作る仕組みです。

継承を使うと、共通する処理を親クラスにまとめ、差分だけを子クラスに書くことができます。

基本的な継承の書き方は次のとおりです。

Python
class 親クラス名:
    ...

class 子クラス名(親クラス名):
    ...

動物を表すクラスを例に、簡単な継承を見てみます。

Python
class Animal:
    def __init__(self, name):
        self.name = name

    def move(self):
        print(f"{self.name}は動きます。")


class Dog(Animal):
    def bark(self):
        print(f"{self.name}は『ワン!』と吠えます。")


dog = Dog("ポチ")
dog.move()  # 親クラスのメソッドをそのまま使える
dog.bark()  # 子クラスで追加したメソッド
実行結果
ポチは動きます。
ポチは『ワン!』と吠えます。

このように、共通の性質を持つクラスを親クラスにまとめ、具体的な種類ごとに子クラスを作ることで、コードの重複を減らし、構造を整理できます。

ただし、むやみに継承を使うとクラス階層が複雑になりやすいため、「本当に親子関係といえるか」を意識して使いどころを判断することが大切です。

オーバーライドで振る舞いを上書きする

継承した子クラスでは、親クラスのメソッドをそのまま使うだけでなく、同じ名前のメソッドを定義し直すことで振る舞いを上書きできます。

これをオーバーライドと呼びます。

オーバーライドの例を見てみます。

Python
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        # 親クラスでのデフォルトの振る舞い
        print(f"{self.name}は何かを話します。")


class Dog(Animal):
    def speak(self):
        # オーバーライドして、犬専用の振る舞いに変更
        print(f"{self.name}は『ワン!』と吠えます。")


class Cat(Animal):
    def speak(self):
        # オーバーライドして、猫専用の振る舞いに変更
        print(f"{self.name}は『ニャー!』と鳴きます。")


animals = [Dog("ポチ"), Cat("タマ")]

for animal in animals:
    animal.speak()
実行結果
ポチは『ワン!』と吠えます。
タマは『ニャー!』と鳴きます。

このように、同じインターフェース(ここではspeakメソッド)を持ちながら、クラスごとに異なる振る舞いをさせられるのがオーバーライドの特徴です。

これがオブジェクト指向におけるポリモーフィズム(多態性)の基本的な使い方です。

なお、親クラスのメソッドを一部だけ変更して使いたい場合には、super()を使って親のメソッドを呼び出した上で、追加の処理を書くこともできます。

Python
class LoggingDog(Dog):
    def speak(self):
        print("ログ: 犬が吠えようとしています。")
        super().speak()  # 親クラス(Dog)の speak を呼ぶ

具象クラスと抽象クラス

クラスには、実際にインスタンスを作って使う「具象クラス」と、インスタンスを直接作らず、共通のインターフェースや基本構造だけを定義する「抽象クラス」という考え方があります。

Pythonでは、標準ライブラリのabcモジュールを使うことで抽象クラスを定義できます。

抽象クラスには@abstractmethodを付けたメソッドを含めることができ、そのクラスを継承する子クラスは、必ずそのメソッドを実装しなければならないというルールを表現できます。

簡単な例を見てみます。

Python
from abc import ABC, abstractmethod


class Shape(ABC):
    """図形を表す抽象クラス"""

    @abstractmethod
    def area(self):
        """面積を返す抽象メソッド"""
        pass


class Rectangle(Shape):
    """長方形の具象クラス"""

    def __init__(self, width, height):
        self.width = width
        self.height = height

    # 抽象メソッドを実装しないとエラーになる
    def area(self):
        return self.width * self.height


rect = Rectangle(3, 4)
print(rect.area())
実行結果
12

Shapeは抽象クラスで、areaメソッドが抽象メソッドとして定義されています。

そのためShape自体をインスタンス化することはできず、必ずRectangleのような具象クラスを経由して使うことになります。

これにより、「図形ならareaメソッドを必ず持っている」という約束を型として表現できます。

データクラス(dataclass)でシンプルなclassを書く

Python 3.7以降では、データ中心のシンプルなクラスを簡潔に書ける@dataclassという仕組みが導入されています。

データクラスを使うと、__init____repr____eq__などのメソッドを自動生成してくれるため、コードを大幅に短くできます。

通常のクラスで書くと次のようになる定義を、

Python
class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"User(name={self.name!r}, age={self.age!r})"

    def __eq__(self, other):
        if not isinstance(other, User):
            return NotImplemented
        return self.name == other.name and self.age == other.age

データクラスを使うと、次のように大幅に省略できます。

Python
from dataclasses import dataclass


@dataclass
class User:
    name: str
    age: int


user1 = User("太郎", 20)
user2 = User("太郎", 20)
user3 = User("花子", 25)

print(user1)            # __repr__ が自動生成される
print(user1 == user2)   # __eq__ も自動生成
print(user1 == user3)
実行結果
User(name='太郎', age=20)
True
False

このように「単にデータを表すだけのクラス」を作りたいときには、dataclassを使うと非常に便利です。

フィールドに型ヒントを付けることが前提になるため、エディタの補完や静的解析ツールとも相性が良くなります。

まとめ

classは、Pythonでオブジェクト指向プログラミングを行うための中核的な仕組みであり、「データ(属性)」と「振る舞い(メソッド)」をひとまとめにできる設計図です。

インスタンス生成とコンストラクタ、selfの意味、カプセル化や継承・オーバーライドといったオブジェクト指向の基本要素、さらに抽象クラスやdataclassまで押さえることで、現実世界の構造に近い形でプログラムを組み立てられるようになります。

まずは小さなクラスから実際に書いて動かしながら、少しずつ自分なりの設計感覚を身につけていくことが大切です。

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

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!