第11章 クラス

クラスの定義と初期化

クラスの定義

Pythonでクラスを定義するには、classキーワードを使用します。

class ClassName:
    pass

ここでClassNameは任意のクラス名です。Pythonの慣例では、クラス名はCamelCase(単語の先頭が大文字)で記述します。


クラスの初期化:__init__ メソッド

クラスを初期化するためには、__init__という特殊なメソッドを定義します。このメソッドは、クラスのインスタンスが生成される際に自動的に呼び出されます。

__init__メソッドの第一引数は常にselfであり、これは生成されるインスタンス自体を参照します。このself引数に続いて、任意の数の追加引数を取ることができます。

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

上記の例では、Personクラスは2つのパラメータnameageを取る__init__メソッドを持っています。これらのパラメータは、クラスのインスタンス変数self.nameself.ageに格納されます。


インスタンスの作成

クラスからインスタンスを作成するには、クラス名を関数のように呼び出し、必要な引数を渡します。

# Person クラスのインスタンスを作成
person1 = Person("Alice", 30)

上記のコードで、Personクラスのインスタンスperson1が作成され、__init__メソッドが自動的に呼び出されます。この結果、person1.name"Alice"という値を、person1.age30という値を持ちます。


変数とメソッド

クラス変数

クラス変数とはクラス全体で共有される変数で、クラスのインスタンス間で共有される値を持つことができる。

class Sample:
    class_var = "これはクラス変数です"  # クラス変数


インスタンス変数とはクラスのインスタンスごとに持つ変数で、各インスタンスで異なる値を持つことができる

class Sample:
    def __init__(self, value):
        self.instance_var = value  # インスタンス変数


インスタンスメソッド

インスタンスメソッドとは一般的なメソッドのタイプで、第一引数としてselfを受け取る。これはメソッドを呼び出すインスタンスを参照します。

class Sample:
    def instance_method(self):
        return "これはインスタンスメソッドです"


クラスメソッド

クラスメソッドとはクラスレベルで動作するメソッドで、@classmethodデコレータで修飾され、第一引数としてclsを受け取る。

class Sample:
    @classmethod
    def class_method(cls):
        return "これはクラスメソッドです"


スタティックメソッド

スタティックメソッドとはクラスレベルで動作するメソッドで、インスタンスやクラス情報にアクセスしないメソッドで、@staticmethodデコレータで修飾される。

class Sample:
    @staticmethod
    def static_method():
        return "これはスタティックメソッドです"


継承

継承は、あるクラス(基底クラス)の属性やメソッドを別のクラス(派生クラス)が受け継ぐ仕組みを指します。継承を使用することで、既存のコードの再利用や拡張が容易になり、プログラムのモジュール性が向上します。


基本的な継承

基底クラスの属性やメソッドは、派生クラスで自動的に利用できるようになります。

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

    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return f"{self.name} はワンワンと鳴く"

dog = Dog("ポチ")
print(dog.speak())  # "ポチ はワンワンと鳴く" と出力

上記の例では、DogクラスはAnimalクラスを継承しています。speakメソッドはDogクラスでオーバーライド(上書き)されています。


super()関数

super()関数は、基底クラスのメソッドを呼び出すのに役立ちます。これは特に、派生クラスでメソッドをオーバーライドするが、基底クラスのメソッドの一部の動作も保持したい場合に役立ちます。

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # Animal の __init__ メソッドを呼び出す
        self.breed = breed

上記の例では、Dogクラスの__init__メソッド内でsuper()を使用して、Animalクラスの__init__メソッドを呼び出しています。


多重継承

Pythonは多重継承をサポートしています。これにより、複数の基底クラスから継承することが可能です。

class A:
    def method_A(self):
        return "A のメソッド"

class B:
    def method_B(self):
        return "B のメソッド"

class C(A, B):
    pass

c = C()
print(c.method_A())  # "A のメソッド" と出力
print(c.method_B())  # "B のメソッド" と出力

上記の例では、クラスCはクラスABの両方を継承しています。


カプセル化とアクセス制限

カプセル化とアクセス制限は、オブジェクト指向プログラミングの重要な概念の一つです。これにより、データを安全に管理し、外部からの不適切なアクセスや変更を防ぐことができます。


カプセル化

カプセル化は、データ(変数)とそのデータを操作するためのメソッド(関数)を一つの「カプセル」(クラス)としてまとめることを指します。これにより、オブジェクトの内部構造や実装の詳細を隠蔽し、必要なインターフェースだけを外部に公開することができます。


アクセス制限

ythonでは、変数やメソッドの名前の前に特定のプレフィックスを付けることでアクセスの制限を行います。

プライベート(Private): 名前の前にアンダースコアを2つ (__) 付けると、その変数やメソッドはクラス外部から直接アクセスできなくなります。

class MyClass:
    def __init__(self):
        self.__private_variable = "プライベート変数"
    
    def __private_method(self):
        return "プライベートメソッド"

このように定義すると、__private_variableおよび__private_methodはクラスの外部から直接アクセスできません。ただし、名前のマングリングにより、_MyClass__private_variableのような形式でアクセスすることは可能です。

プロテクテッド(Protected): 名前の前にアンダースコアを1つ (_) 付けると、その変数やメソッドは「プロテクテッド」と見なされます。これは、サブクラスや同じモジュール内からのみアクセスすべきであることを示す慣習的なものですが、実際にアクセスを制限するものではありません。

class MyClass:
    def __init__(self):
        self._protected_variable = "プロテクテッド変数"
    
    def _protected_method(self):
        return "プロテクテッドメソッド"


特殊メソッドとオーバーロード

Pythonの特殊メソッドは、特定の構文やビルトイン関数を使ってオブジェクトと対話するためのメソッドです。これらは「__」(アンダースコア2つ)で始まり、「__」で終わる名前を持っています。オーバーロードとは、これらの特殊メソッドを自分のクラスで再定義し、デフォルトの動作を変更することを指します。


__init__: 初期化メソッド

オブジェクトが生成される際に呼び出されるメソッドです。

class Example:
    def __init__(self, value):
        self.value = value


__str__: 文字列表現

str()関数やprint()関数でオブジェクトを表示したときの文字列表現を返します。

class Example:
    def __init__(self, value):
        self.value = value
        
    def __str__(self):
        return f"Example({self.value})"


__repr__: 開発者向けの文字列表現

オブジェクトの「公式」文字列表現を返します。これは主にデバッグや開発で使用されます。

class Example:
    # (中略)
    def __repr__(self):
        return f"Example({repr(self.value)})"


演算子オーバーロード

演算子を使用するときの動作をカスタマイズするためのメソッドです。

# 加算のオーバーロード
class Example:
    def __init__(self, value):
        self.value = value
        
    def __add__(self, other):
        if isinstance(other, Example):
            return Example(self.value + other.value)
        return NotImplemented


__getitem__と__setitem__: インデックスアクセス

オブジェクトに対してブラケットを使ってアクセスしたときの動作を定義します。

class Example:
    def __init__(self, data):
        self.data = data
        
    def __getitem__(self, key):
        return self.data[key]
        
    def __setitem__(self, key, value):
        self.data[key] = value


練習問題1.

銀行口座を模倣したBankAccountクラスを作成してください。このクラスには以下のメソッドを持つものとします。

  • deposit(amount): 口座に指定された金額を預ける。
  • withdraw(amount): 口座から指定された金額を引き出す。ただし、残高が不足している場合は引き出せない。
  • balance(): 現在の残高を返す。
class BankAccount:
    # この部分を完成させてください

account = BankAccount()
account.deposit(1000)
account.withdraw(500)
print(account.balance())  # この結果は500となるべき


練習問題2.

以下の2つのクラスを作成してください。

  • Rectangle: 長方形を表すクラス。初期化時に幅と高さを受け取る。面積を計算するareaメソッドを持つ。
  • Circle: 円を表すクラス。初期化時に半径を受け取る。面積を計算するareaメソッドを持つ。円周率は3.14159とする。
class Rectangle:
    # この部分を完成させてください

class Circle:
    # この部分を完成させてください

rectangle = Rectangle(10, 5)
circle = Circle(7)
print(rectangle.area())  # この結果は50となるべき
print(circle.area())     # この結果は約153.938となるべき


練習問題3.

Personクラスを基底クラスとして、そのサブクラスとしてStudentクラスを作成してください。Personクラスはname属性と、自己紹介をするintroduceメソッドを持ちます。Studentクラスは追加でgrade属性を持ち、introduceメソッドをオーバーライドして、学年も紹介するようにしてください。

class Person:
    # この部分を完成させてください

class Student(Person):
    # この部分を完成させてください

person = Person("Taro")
student = Student("Hanako", 3)
person.introduce()  # この結果は"Taroです。"となるべき
student.introduce() # この結果は"Hanakoです。3年生です。"となるべき