スキルアップ

Python入門:オブジェクト指向、クラスやインスタンス

はじめに

Pythonの学習も進んでくると、
変数や関数だけではコードが複雑になっていきます。

そんな中で、関連するデータと処理をひとまとめ
にしたくなる場面が増えてきます。

例えば、人の情報を扱うプログラムにおいて、
名前や年齢といったデータ(変数)と、
歩く・話すといった処理(関数)がバラバラに存在していると、
管理が煩雑になります。

そんなときに役立つのが、Pythonでも採用されている
オブジェクト指向という考え方です。

オブジェクト指向を使えば、

  • データ(変数)と、そのデータを扱う処理
    (メソッド)をひとまとめにできる
  • 関連する情報を
    「ひとつのまとまり(オブジェクト)」
    として管理できる
  • クラスの「継承」によって、
    コードを再利用しやすくなる

といったメリットがあり、副業や実務で通用する
Pythonスキルを身につけるためには
避けて通れない重要な考え方
です。

今回の記事では、私が実際に学んだ内容から

  • オブジェクトという考え方の基本
  • クラスの定義方法
  • インスタンス変数とクラス変数の違い
  • コンストラクタの役割とselfの重要性
  • クラスの継承や型確認の方法

といった基礎をまとめていきます。

この記事はこんな方におすすめです!

  • Pythonで「オブジェクト指向」が
    まだよく理解できていない方
  • クラス定義や継承を基礎から学びたい方
  • 実務や副業を意識して、
    整理されたコードを書けるようになりたい方


第1章:オブジェクトという考え方

Pythonをはじめ、多くのプログラミング言語は
オブジェクト指向という考え方を採用しています。

オブジェクトとは、簡単に言うと
データ(属性)と、それを操作するための処理(メソッド)
をひとまとめにしたもの
です。

例えば、「人」というオブジェクトを考えてみましょう。

  • データ(属性):名前、年齢、身長
  • 処理(メソッド):歩く、話す、自己紹介する

これらをひとつの「人オブジェクト」としてまとめることで、
どんなデータを持っていて、どんな動きをするのか
が直感的に管理しやすくなります。


オブジェクト

オブジェクトという言葉自体が「もの」「対象」を意味します。

Pythonにおいては、すべてがオブジェクトです。

数値(int)、文字列(str)、リスト(list
などのデータ型も、
実際にはそれぞれがクラスから作られたオブジェクトです。

x = 10
print(type(x))  # → <class 'int'>

s = "Hello"
print(type(s))  # → <class 'str'>

つまり、Pythonで今までの学習で扱ってきた数値や文字列も、
実はすべてクラスという設計図から作られたオブジェクトだった
ということになります。


関数型との違い

これまで学んできた、関数ベースの書き方では、
データと処理が分離していました。

関数型(今までの学習でのコードの書き方)

name = "太郎"
age = 25

def introduce(n, a):
    print(f"{n}さんは{a}歳です")

introduce(name, age)

これを、変数と関数をバラバラにせず
オブジェクト指向的にまとめるとすると以下のような形になります。

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

    def introduce(self):
        print(f"{self.name}さんは{self.age}歳です")

p1 = Person("太郎", 25)
p1.introduce()

このコードの例では、データと処理が
「Person」というひとつのオブジェクトに整理
されています。

classキーワードなど、オブジェクト指向を実現するための
プログラムの書き方はこの後の章でまとめていきますが、
このように書くことで、データと処理をセットにでき、
プログラムの管理のしやすさや汎用性が、格段に上がります。


オブジェクト指向のメリット

  • コードが整理され、見通しが良くなる
    関連するデータと処理を
    同じ場所にまとめるので分かりやすい
  • 再利用性が高まる
    一度作ったクラス(設計図)から、
    同じ仕組みを持つオブジェクトを何個でも作れる
  • 変更に強い
    クラス(設計図)を変更するだけで、
    そこから作られたオブジェクトすべてに
    反映される

第2章:クラス定義の基本

まずクラス(class)とは、
オブジェクトを作るための設計図のようなものです。

オブジェクト指向でのオブジェクトを作るためには
このクラスが欠かせないものになります。

オブジェクトを作るためには、

  • 設計図(クラス)
    → 実際に作られたもの(インスタンス、オブジェクト)

といった手順を踏むことになります。

Pythonでもオブジェクト指向を扱う場合、
まずはクラスを定義し、そこからインスタンスを作ります。


クラス定義の基本構文

Pythonでのクラス定義は次のように書きます。

class クラス名:
    クラス変数 = 値

    def メソッド名(self, 引数):
        処理内容
  • class キーワードで定義を開始
  • メソッド(クラス内で定義する関数)は
    必ず1つ目の引数にselfを指定

クラス定義例

class Dog:
    kind = "柴犬"  # クラス変数

    def bark(self):
        print("ワンワン!")

# インスタンス化(オブジェクト生成)
dog1 = Dog()

print(dog1.kind)  # → 柴犬
dog1.bark()       # → ワンワン!

クラス変数とインスタンス変数の違い

クラスをつくることで、
変数と処理をまとめることができるようになりますが
変数にも、以下の種類があります。

  • クラス変数
    • クラス全体で共有される変数
    • どのインスタンスからも同じ値を参照する
  • インスタンス変数
    • 各インスタンスごとに異なる値を持てる変数
    • self.変数名 = 値 の形で、通常はコンストラクタ(__init__)で初期化する

インスタンス変数を持つクラス例:

class Cat:
    species = "ネコ科"  # クラス変数

    def __init__(self, name, age):
        self.name = name          # インスタンス変数
        self.age = age            # インスタンス変数

    def meow(self):
        print(f"{self.name}({self.age}歳)がニャーと鳴きました")

# インスタンス化
cat1 = Cat("ミケ", 2)
cat2 = Cat("タマ", 3)

print(cat1.species, cat1.name)  # → ネコ科 ミケ
print(cat2.species, cat2.name)  # → ネコ科 タマ

cat1.meow()  # → ミケ(2歳)がニャーと鳴きました
cat2.meow()  # → タマ(3歳)がニャーと鳴きました

クラス変数とインスタンス変数の比較表

項目クラス変数インスタンス変数
定義場所クラス直下__init__などメソッド内でselfを使って定義
共有範囲すべてのインスタンスで共通各インスタンスごとに異なる値
主な用途種類、分類など全体で共通する情報名前、年齢など個別に異なる情報
呼び出し方インスタンス.変数名 または クラス名.変数名インスタンス.変数名

また、クラス変数については、インスタンスを作らなくても
以下のようにクラス名から直接アクセスできます。

print(Cat.species)  # → ネコ科

第3章:コンストラクタの仕組み

クラスからオブジェクトを作成するとき、
オブジェクトの初期状態を設定する役割
を担うのがコンストラクタです。

Pythonでは、このコンストラクタを
__init__ メソッド で定義します。

  • オブジェクト生成時に自動で呼び出される
  • インスタンス変数を初期化するために
    使うことが多い

コンストラクタの基本構文

class クラス名:
    def __init__(self, 引数1, 引数2, ...): #コンストラクタ
        self.変数名 = 引数1
        self.変数名 = 引数2

具体例:

class Person:
    def __init__(self, name, age):
        self.name = name      # インスタンス変数に代入
        self.age = age

person1 = Person("太郎", 25)   # オブジェクト生成、初期化
print(person1.name, person1.age)  # → 太郎 25

selfの意味と役割

self は、作成されたそのオブジェクト自身を指す変数です。

  • self.name と書くことで、
    「このオブジェクトの name 変数」という意味
  • クラスのインスタンスごとにselfは異なり、
    それぞれのオブジェクトが独自に値を持つ

複数インスタンスを作成した場合が
このselfのイメージが付きやすいと思います。

先ほどのPersonクラスをもとに
2つのインスタンスを作成します。

p1 = Person("花子", 30)
p2 = Person("健太", 22)

print(p1.name)  # → 花子
print(p2.name)  # → 健太

このように、p1p2 は同じクラスから作られていますが、
インスタンス変数は個別に管理されるようになります。


コンストラクタとクラス変数の違い

クラス変数は変更するとすべてのインスタンスに影響しますが、
インスタンス変数はそれぞれ独立しています。

class Dog:
    species = "柴犬"  # クラス変数(共通)

    def __init__(self, name):
        self.name = name  # インスタンス変数(個別)

d1 = Dog("ポチ")
d2 = Dog("タロウ")

print(d1.species, d1.name)  # → 柴犬 ポチ
print(d2.species, d2.name)  # → 柴犬 タロウ

Dog.species = "秋田犬"  # クラス変数を変更
print(d1.species, d2.species)  # → 秋田犬 秋田犬
項目コンストラクタで作るインスタンス変数クラス変数
値の管理単位インスタンスごとに独立クラス全体で共有
初期化の方法__init__self.変数名 = 値クラス直下で定義

コンストラクタの引数のデフォルト値

引数にデフォルト値を設定しておけば、
インスタンス作成時に引数の省略時にも動作します。

class Animal:
    def __init__(self, name="不明", age=0):
        self.name = name
        self.age = age

a1 = Animal()
print(a1.name, a1.age)  # → 不明 0

第4章:メソッドとselfのルール

関数の定義方法はクラスでも同様defキーワードを用います。

またクラス内で定義する関数のことを、
Pythonではメソッドと呼びます。

  • メソッドは、そのクラスから作られた
    オブジェクトに関連する処理を実装
  • 例えば「人」クラスなら「話す」「歩く」
    といった動作がメソッドに相当

selfの必要性

Pythonのインスタンスメソッドは、
必ず最初の引数にselfを指定します。

class Person:
    def greet(self):
        print("こんにちは!")

理由として、

  • self は、そのメソッドが呼ばれた
    インスタンス自身を表す
    特別な変数のため
  • これにより、同じクラスから作られた
    別々のインスタンスが、
    それぞれ自分のデータを扱える
    ようになる

selfを使ったインスタンス変数へのアクセス

self.変数名 の形で書くことで、
そのインスタンス固有の変数にアクセスします。

class Person:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"{self.name}さん、こんにちは!")

p1 = Person("太郎")
p2 = Person("花子")

p1.greet()  # → 太郎さん、こんにちは!
p2.greet()  # → 花子さん、こんにちは!

ここでのポイントは、self.name
それぞれのインスタンスのname変数を指している
ことです。


selfを書かない場合の挙動

クラス内でのメソッド定義でselfを引数に書き忘れると、
Pythonは自動でインスタンスを渡せないため、
エラーになります。

class Sample:
    def test():  # selfを書き忘れた!
        print("テスト")

s = Sample()
s.test()  
# → TypeError: test() takes 0 positional arguments but 1 was given

Pythonは、s.test() と書くと
自動的にs(インスタンス)を最初の引数として渡すため、
メソッド側でselfを受け取る準備が必要です。


selfの中身

selfはそのインスタンス自身なので、
id()関数などでオブジェクトのアドレスを確認すると
同じになることがわかります。

class Sample:
    def show_self(self):
        print(id(self))

obj = Sample()
print(id(obj))      # インスタンス自体のID
obj.show_self()     # メソッド内のselfのID

出力結果:

140662888934992
140662888934992  ← 同じ

selfは慣習的な名前

selfという名前はPythonの慣習であり、
実際には別の名前を使うことも可能です。

しかし、可読性が落ちるため
selfを使うのが推奨
されています。

class Person:
    def greet(this):  # 動くが推奨されない
        print("こんにちは!")

第5章:クラスの継承

継承とは、既存のクラス
(親クラス、スーパークラスとも呼びます)
の機能を引き継いで、
新しいクラス(子クラス、サブクラス)を作る仕組みです。

継承を使うことで、

  • 共通する処理を親クラスにまとめておき、
    コードを再利用できる
  • 子クラスで必要に応じて機能を追加・変更できる

というメリットがあります。


継承の基本構文

class 親クラス名:
    # 親クラスの処理

class 子クラス名(親クラス名):
    # 親クラスを継承した処理

継承の実例として、
「動物」という親クラスから、特定の動物クラスを作ってみます。

親クラス(Animal):

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

    def eat(self):
        print(f"{self.name}は食事をしています")

子クラス(DogCat):

class Dog(Animal):  # Animalを継承
    def bark(self):
        print(f"{self.name}がワンワンと吠えています")
class Cat(Animal):  # Animalを継承
    def meow(self):
        print(f"{self.name}がニャーと鳴いています")

親クラスを継承することで、
子クラスではそれぞれ親のメソッドを呼び出せるようになります。

dog = Dog("ポチ")
dog.eat()   # → ポチは食事をしています(親クラスのメソッドをそのまま利用)
dog.bark()  # → ポチがワンワンと吠えています

cat = Cat("ミケ")
cat.eat()   # → ミケは食事をしています
cat.meow()  # → ミケがニャーと鳴いています

親クラスのメソッドをオーバーライド(上書き)

親から引き継いだメソッドの内容を
子クラス側で個別に実装することができます。

同じメソッド名で呼び出してもそれぞれのクラスにあった
処理内容に変えることが可能です。(オーバーライド)

class Dog(Animal):
    def eat(self):  # 親クラスのeatを上書き
        print(f"{self.name}はドッグフードを食べています")
dog = Dog("ポチ")
dog.eat()  # → ポチはドッグフードを食べています

親クラスのメソッドを呼び出したい場合はsuper()

上書きした上で、親クラスのメソッドも呼びたいときは
super()を使います。

class Dog(Animal):
    def eat(self):
        super().eat()  # 親クラスのeatを呼び出す
        print(f"{self.name}はおかわりを欲しがっています")
dog = Dog("ポチ")
dog.eat()

出力結果:

ポチは食事をしています
ポチはおかわりを欲しがっています

type() と isinstance() の使い分け

継承したクラスのオブジェクトの型を確認するとき、
type()isinstance() では動作が異なります。

ポイントとして、

  • type()
    オブジェクトが
    「その型と完全一致するか」を確認
  • isinstance()
    継承関係を含めて確認できる

継承を意識した型確認では、
isinstance()を使うのが一般的です。

dog = Dog("ポチ")

print(type(dog))           # → <class '__main__.Dog'>
print(type(dog) == Animal) # → False(継承元までは確認できない)

print(isinstance(dog, Dog))    # → True
print(isinstance(dog, Animal)) # → True(親クラスでもTrueになる)

第6章:まとめ&次回予告


今回は、Pythonにおけるオブジェクト指向の基礎として、
オブジェクトとクラス定義を中心に学びました。

今回のポイントとして、

  • オブジェクトとは、データ(属性)と
    処理(メソッド)をひとまとまりにしたもの
  • クラスはオブジェクトを作るための設計図
  • クラス変数は全インスタンスで共有、
    インスタンス変数は各インスタンスごとに独立
  • コンストラクタ(__init__)で初期化し、
    selfでそのオブジェクト自身を参照
  • 継承を使えば、親クラスの機能を引き継ぎつつ、
    子クラスで機能追加・上書きが可能
  • 型確認では、isinstance()
    継承関係も考慮できるため便利

オブジェクト指向は、コードを整理して再利用性を高める
非常に強力な仕組みです。

最初はとっつきにくい部分もありますが、
副業や実務での開発では
必ず押さえておかなければいけないポイントです。

次回は、今回学んだ基礎をさらに発展させ、
クラスの応用的な使い方を解説します。

  • 特殊メソッド__str____len__など)
    の活用
  • クラスメソッドスタティックメソッドの違い
  • クラス変数を活用したオブジェクト管理の実例

こうした応用テクニックを学ぶことで、
よりPythonらしいクラス設計ができるようになります。

▶次回の記事はこちら:
[現在準備中です!少々お待ちください!]

-スキルアップ