Python入門 第14回:クラス入門

Python入門 第14回:クラス入門 Python

はじめに

Pythonの学習を進めていくと、避けては通れないのが「クラス」です。

クラスは、初心者にとって少し難しく感じやすいポイントです。

class
__init__
self

このような見慣れない記述が一度に登場するため、急に難しくなったように感じるかもしれません。

しかし、クラスの基本的な考え方はそれほど複雑ではありません。

クラスとは、簡単にいえば、データと処理をひとまとめにするための仕組みです。

この記事では、クラスの基本から、インスタンス、__init__selfの役割まで、初心者向けに順番に解説します。

クラスとは

クラスとは、データや処理をまとめるための設計図のようなものです。

例えば、ゲームに登場するキャラクターをPythonで管理するとします。

キャラクターには、次のような情報があります。

  • 名前
  • HP
  • 攻撃力

また、キャラクターが行う処理として、次のようなものがあります。

  • 自己紹介する
  • 攻撃する
  • ダメージを受ける

これらのデータと処理を、ひとまとめにして管理できるのがクラスです。

クラスを使わずにキャラクターの情報を管理する場合、次のように複数の変数を用意することになります。

character_name = "スライム"
character_hp = 100
character_attack = 20

キャラクターが1体だけであれば、この書き方でも問題ありません。

しかし、キャラクターが増えると変数の数も増えていきます。

character1_name = "スライム"
character1_hp = 100
character1_attack = 20

character2_name = "ドラゴン"
character2_hp = 500
character2_attack = 80

キャラクターが10体、100体と増えていくと、管理が難しくなります。

このような場合に、クラスを使うと関連する情報をまとめて管理できます。

クラスの基本的な書き方

Pythonでは、classを使ってクラスを定義します。

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

class クラス名:
    処理

実際に簡単なクラスを作ってみましょう。

class Character:
    pass

Characterという名前のクラスを定義しています。

クラス名は自由に決められますが、Pythonではクラス名の先頭を大文字にするのが一般的です。

例えば、次のように単語の先頭を大文字にして書きます。

class Character:
    pass
class PlayerCharacter:
    pass

このような書き方を、パスカルケースと呼びます。

また、passは「何もしない」という意味です。

Pythonでは、クラスの中を空のままにできません。

そのため、クラスの内容をあとから書く場合などに、仮の処理としてpassを記述します。

インスタンスとは

クラスを定義しただけでは、まだ実際に使えるデータは作られていません。

クラスという設計図をもとに、実際のデータを作る必要があります。

クラスから作られた実際のデータを、インスタンスと呼びます。

class Character:
    pass


character = Character()

このコードでは、Characterクラスからインスタンスを作成しています。

Character()

クラス名の後ろに丸括弧を書くことで、インスタンスを作成できます。

作成したインスタンスは、変数に代入して使用します。

character = Character()

ここでは、作成したインスタンスをcharacterという変数に代入しています。

クラスとインスタンスの関係は、次のように考えると分かりやすいです。

  • クラス:設計図
  • インスタンス:設計図から作った実物

例えば、たい焼きの型がクラスで、その型を使って作ったたい焼きがインスタンスです。

同じ型から、複数のたい焼きを作ることができます。

同じように、1つのクラスから複数のインスタンスを作成できます。

class Character:
    pass


character1 = Character()
character2 = Character()
character3 = Character()

character1character2character3は、すべてCharacterクラスから作られた別々のインスタンスです。

属性を持たせる

インスタンスには、名前やHPなどのデータを持たせることができます。

インスタンスが持つデータを、属性と呼びます。

属性は、次のように設定できます。

class Character:
    pass


character = Character()

character.name = "スライム"
character.hp = 100

print(character.name)
print(character.hp)

実行結果は次のとおりです。

スライム
100

属性にアクセスするときは、インスタンス名と属性名をドットでつなぎます。

インスタンス名.属性名

値を設定する場合は、次のように書きます。

character.name = "スライム"

値を取得する場合も、同じようにドットを使います。

print(character.name)

ただし、インスタンスを作成したあとに毎回属性を設定するのは少し面倒です。

character1 = Character()
character1.name = "スライム"
character1.hp = 100

character2 = Character()
character2.name = "ドラゴン"
character2.hp = 500

このような処理を簡単にするために使われるのが、__init__です。

__init__とは

__init__は、インスタンスが作成されたときに自動的に実行される特別なメソッドです。

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

class クラス名:
    def __init__(self):
        処理

実際に書いてみましょう。

class Character:
    def __init__(self):
        print("キャラクターを作成しました")


character = Character()

実行結果は次のとおりです。

キャラクターを作成しました

コード上では、__init__を直接呼び出していません。

それでも、次の処理によってインスタンスを作成したときに、__init__が自動的に実行されます。

character = Character()

__init__は、主にインスタンスの初期設定に使用します。

例えば、キャラクターの名前とHPを設定してみましょう。

class Character:
    def __init__(self):
        self.name = "スライム"
        self.hp = 100


character = Character()

print(character.name)
print(character.hp)

実行結果は次のとおりです。

スライム
100

インスタンスを作成した時点で、namehpという属性が自動的に設定されています。

なお、__init__の前後には、アンダースコアが2つずつ付いています。

__init__

次のように、アンダースコアが1つだけではありません。

_init_

入力するときは間違えないように注意しましょう。

selfとは

クラスを初めて学ぶときに、特に分かりにくいのがselfです。

selfは、作成されたインスタンス自身を表します。

次のコードを見てみましょう。

class Character:
    def __init__(self):
        self.name = "スライム"

このコードのself.nameは、作成されたインスタンスが持つname属性を意味します。

character = Character()

この場合、イメージとしては次のようになります。

character.name = "スライム"

ただし、クラスを定義している時点では、インスタンスの変数名がcharacterになるとは限りません。

次のように、さまざまな変数名でインスタンスが作成される可能性があります。

character1 = Character()
enemy = Character()
player = Character()

そのため、クラスの中では特定の変数名を使わず、インスタンス自身を表すselfを使用します。

self.name

selfは、メソッドの最初の引数として記述します。

def __init__(self):

ただし、インスタンスを作るときに、selfに渡す値を指定する必要はありません。

character = Character()

Pythonが自動的に、作成したインスタンスをselfに渡してくれます。

そのため、次のようには書きません。

character = Character(character)

selfはクラスの中で使用する特別な変数だと考えておきましょう。

メソッドを定義する

クラスの中に定義された関数を、メソッドと呼びます。

通常の関数は、次のように定義します。

def hello():
    print("こんにちは")

クラスの中に関数を書くと、その関数はメソッドになります。

class Character:
    def hello(self):
        print("こんにちは")

メソッドを呼び出すときは、インスタンス名とメソッド名をドットでつなぎます。

class Character:
    def hello(self):
        print("こんにちは")


character = Character()
character.hello()

実行結果は次のとおりです。

こんにちは

メソッドの中では、selfを使ってインスタンスの属性にアクセスできます。

class Character:
    def __init__(self):
        self.name = "スライム"

    def introduce(self):
        print(f"私は{self.name}です")


character = Character()
character.introduce()

実行結果は次のとおりです。

私はスライムです

introduceメソッドの中で、self.nameを使ってインスタンスの名前を取得しています。

クラスの中では、属性とメソッドをまとめて管理できます。

class Character:
    def __init__(self):
        self.name = "スライム"
        self.hp = 100

    def introduce(self):
        print(f"名前は{self.name}です")
        print(f"HPは{self.hp}です")

このように、キャラクターに関するデータと処理を、1つのクラスにまとめられます。

引数を使ってインスタンスを作る

先ほどのクラスでは、どのインスタンスを作成しても、名前が「スライム」、HPが100になります。

class Character:
    def __init__(self):
        self.name = "スライム"
        self.hp = 100

異なる名前やHPを持つキャラクターを作るには、__init__に引数を追加します。

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

インスタンスを作るときに、名前とHPを渡します。

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp


character = Character("スライム", 100)

print(character.name)
print(character.hp)

実行結果は次のとおりです。

スライム
100

次の部分で、Characterクラスに値を渡しています。

Character("スライム", 100)

渡された値は、__init__namehpで受け取ります。

def __init__(self, name, hp):

そして、受け取った値をインスタンスの属性に代入します。

self.name = name
self.hp = hp

左側と右側に似た名前が並んでいるため、最初は少し分かりにくいかもしれません。

self.name = name

左側のself.nameは、インスタンスが持つ属性です。

右側のnameは、引数として受け取った値です。

つまり、次のような意味になります。

インスタンスのname属性 = 引数として受け取ったname

HPについても同じです。

self.hp = hp

属性の値を変更する

インスタンスの属性は、作成後に変更できます。

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp


character = Character("スライム", 100)

character.hp = 80

print(character.hp)

実行結果は次のとおりです。

80

また、メソッドを使って属性の値を変更することもできます。

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def damage(self, amount):
        self.hp = self.hp - amount


character = Character("スライム", 100)

character.damage(30)

print(character.hp)

実行結果は次のとおりです。

70

damageメソッドには、ダメージ量を表すamountを渡しています。

character.damage(30)

メソッドの中では、現在のHPからダメージ量を引いています。

self.hp = self.hp - amount

この処理は、短く書くこともできます。

self.hp -= amount

コード全体は次のようになります。

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def damage(self, amount):
        self.hp -= amount


character = Character("スライム", 100)

character.damage(30)

print(character.hp)

複数のインスタンスを作る

1つのクラスから、複数のインスタンスを作成できます。

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def introduce(self):
        print(f"{self.name}のHPは{self.hp}です")


character1 = Character("スライム", 100)
character2 = Character("ドラゴン", 500)

character1.introduce()
character2.introduce()

実行結果は次のとおりです。

スライムのHPは100です
ドラゴンのHPは500です

character1character2は、同じCharacterクラスから作られています。

しかし、それぞれ別のインスタンスであるため、異なる属性を持つことができます。

character1.name

には「スライム」が保存されています。

一方で、

character2.name

には「ドラゴン」が保存されています。

一方の属性を変更しても、もう一方の属性には影響しません。

character1.hp = 50

print(character1.hp)
print(character2.hp)

実行結果は次のとおりです。

50
500

このように、クラスを使うことで、同じ種類のデータを効率よく管理できます。

クラスを使わない場合との違い

クラスを使わず、辞書を使ってキャラクターを管理することもできます。

character = {
    "name": "スライム",
    "hp": 100
}

HPを減らす場合は、次のように書けます。

character["hp"] -= 30

小さなプログラムであれば、辞書だけでも十分です。

しかし、データだけでなく処理も増えてくると、クラスを使ったほうが管理しやすくなります。

辞書では、データと関数を別々に定義することになります。

def damage(character, amount):
    character["hp"] -= amount

クラスを使う場合は、データと処理を同じ場所にまとめられます。

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

    def damage(self, amount):
        self.hp -= amount

クラスを使うと、どのデータに対する処理なのかが分かりやすくなります。

character.damage(30)

クラスは、次のような場面でよく使われます。

  • ゲームのキャラクターを管理する
  • 商品情報を管理する
  • ユーザー情報を管理する
  • センサーや機器を管理する
  • GUIの画面やボタンを管理する
  • Webアプリケーションのデータを管理する

プログラムの規模が大きくなるほど、クラスを使う機会も増えていきます。

よくあるエラー

selfを書き忘れる

次のコードには、selfがありません。

class Character:
    def introduce():
        print("こんにちは")


character = Character()
character.introduce()

このコードを実行すると、エラーが発生します。

インスタンスから呼び出すメソッドでは、第1引数にselfを記述します。

class Character:
    def introduce(self):
        print("こんにちは")

属性にselfを付け忘れる

次のコードでは、nameselfが付いていません。

class Character:
    def __init__(self, name):
        name = name

これでは、インスタンスの属性として名前が保存されません。

正しくは次のように書きます。

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

__init__のスペルを間違える

次の書き方は間違いです。

def init(self):

正しくは、前後にアンダースコアを2つずつ付けます。

def __init__(self):

引数の数が合っていない

次のクラスでは、インスタンスを作るときにnamehpを渡す必要があります。

class Character:
    def __init__(self, name, hp):
        self.name = name
        self.hp = hp

そのため、次の書き方ではエラーになります。

character = Character()

必要な値を渡します。

character = Character("スライム", 100)

インデントがずれている

クラスの中の処理には、インデントが必要です。

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

正しくは次のように書きます。

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

メソッドの中の処理は、さらに1段階インデントします。

練習問題

最後に、クラスを使った簡単な練習問題に挑戦してみましょう。

練習問題1

次の情報を持つDogクラスを作成してください。

  • 名前
  • 年齢

また、名前と年齢を表示するintroduceメソッドを作成してください。

実行例は次のとおりです。

dog = Dog("ポチ", 3)
dog.introduce()

実行結果は次のようにします。

名前はポチです
年齢は3歳です

解答例は次のとおりです。

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

    def introduce(self):
        print(f"名前は{self.name}です")
        print(f"年齢は{self.age}歳です")


dog = Dog("ポチ", 3)
dog.introduce()

練習問題2

商品の名前と価格を管理するProductクラスを作成してください。

次の情報を属性として持たせます。

  • 商品名
  • 価格

また、商品情報を表示するshow_infoメソッドを作成してください。

product = Product("キーボード", 5000)
product.show_info()

実行結果は次のようにします。

商品名:キーボード
価格:5000円

解答例は次のとおりです。

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def show_info(self):
        print(f"商品名:{self.name}")
        print(f"価格:{self.price}円")


product = Product("キーボード", 5000)
product.show_info()

練習問題3

銀行口座を表すBankAccountクラスを作成してください。

次の機能を持たせます。

  • 所有者名を保存する
  • 残高を保存する
  • 入金する
  • 現在の残高を表示する

解答例は次のとおりです。

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount

    def show_balance(self):
        print(f"{self.owner}さんの残高は{self.balance}円です")


account = BankAccount("田中", 10000)

account.deposit(5000)
account.show_balance()

実行結果は次のとおりです。

田中さんの残高は15000円です

まとめ

今回は、Pythonのクラスについて解説しました。

クラスは、データと処理をひとまとめにするための仕組みです。

今回の重要なポイントは、次のとおりです。

  • classを使ってクラスを定義する
  • クラスは設計図のようなもの
  • クラスから作られたデータをインスタンスと呼ぶ
  • インスタンスが持つデータを属性と呼ぶ
  • クラスの中に定義した関数をメソッドと呼ぶ
  • __init__はインスタンス作成時に自動実行される
  • selfはインスタンス自身を表す
  • 1つのクラスから複数のインスタンスを作成できる

クラスは、Python入門の中でも大きな山場です。

最初からすべてを完璧に理解しようとすると、難しく感じてしまうかもしれません。

まずは、次の3つを覚えておきましょう。

クラスは設計図
インスタンスは設計図から作った実物
selfはインスタンス自身

実際にコードを書きながら、属性やメソッドを少しずつ追加していくと、クラスの仕組みが分かるようになります。

次回は、クラスの仕組みをさらに活用するために、継承やカプセル化などについて学んでいきます。

コメント

タイトルとURLをコピーしました