オブジェクトとクラス#

基本概念#

今までさまざまな箇所で触れたように、Pythonに含まれるすべての要素がオブジェクトとして扱われます。そのゆえに、pythonはオブジェクト指向プログラミングの一種になります。

オブジェクトとは、データ(属性)とそれを操作する関数(メソッド)をカプセル化したものです。

例えば、「customer」というオブジェクトで考えてみます。

  • customerでは、「email」や「phone」などのプロファイルがあります。このようなcustomerが持つ特徴を「属性」といいます。

  • customerでは、「place order」や「cancel order」などの行動を行うことができます。このようなcustomerが取ることのできるアクションを「メソッド」といいます。

各customer独自の属性とメソッドを持っていますが、「形式」は共通しています。

あるcustomerを定義するために必要される属性とメソッドを定義している「テンプレート」に相当するものは、「クラス」といいます。

「テンプレート」に内容を記入することで、作成される一人一人のcustomerが「インスタンス」といいます。

  • num=10と書けば、値10の整数型のオブジェクトを作って、numという名前にオブジェクト参照を代入できます。整数型のオブジェクトは加算や乗算などのメソッドを持つため、num+10num*10などの計算は可能です。

  • "cat""dog"などの文字列もオプジェットであり、メソッドを持っています。文字列のオプジェットに定義されるメソッドによる、"cat"+"dog"で文字列の結合のような文字列のオプジェット特有な操作が可能です。

Pythonのオブジェクトはクラスに基づいて作成されます。クラスはオブジェクトの設計図であり、オブジェクトが持つデータとメソッドを定義します。

Pythonには、文字列、リスト、辞書などの標準データ型を作るための組み込みクラスがあります。

新しいオブジェクトを作成する時、オブジェクトの内容と機能を規定するクラスを作る必要があります。

classによるクラスの定義#

一般にクラス定義は、以下のような形をしています。

class クラス名:
    def __init__(self):
        実行文
    def メソッド名(self, 引数, ...):
        実行文
    def メソッド名(self, 引数, ...):
        実行文

初期化#

オプジェットを作成する際、なんらかの値を代入することは必要される場合が多いです。

その場合、__init__という特殊なメソッドで、クラスのインスタンス化時に自動的に初期設定を行います。これにより、同じクラスの異なるインスタンスが異なる値を持つことができます。

メソッド#

ここで示したように、メソッドは、クラスの中の関数のことです。

メソッド定義は関数定義と同じ形をしていますが、その最初の引数には慣例としてselfという名前を付けます。 この引数には、メソッドが呼び出されたオブジェクト自身が渡されます。メソッド内で自身の属性や他のメソッドにアクセスするために使用されます。

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

    def say_hello(self):
        print("Hello,", self.name)
marx=Person("Marx")

上記の例では、

  • Personというクラスを定義する

  • Person("Marx")でオブジェクトのインスタンスを生成する

  • selfとして新しい作ったオブジェクト、nameとしてもう一つの引数"Marx"を渡して、オブジェクトの__init()__メソッドを呼び出す

  • nameの値をオブジェクトに格納する

  • 新しいオブジェクトを返す、Marxという名前を与える

オブジェクトにおける値は、属性としてオブジェクトとともに保存されますので、オプジェットの外から直接に読み書きできます。

marx.name
'Marx'

オブジェクトのメソッドを呼び出します。

marx.say_hello()
Hello, Marx
  • ここで注意してほしいのは、Personクラス内部では、name属性にはself.nameという形でアクセスできます。

異なる値を渡せば、Personクラスから新しいオブジェクトを作成できます。

webber=Person("Webber")
webber.name
'Webber'
webber.say_hello()
Hello, Webber

属性とメソッドの追加#

同じような形式で、属性とメソッドの追加しましょう。

例えば、Personクラスbirth属性とprint_birth()メソッドを追加するなら

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

    def say_hello(self):
        print("Hello, my name is", self.name)
    
    def print_birth(self):
        print("{} was born in {}".format(self.name,self.birth))
sato=Person("Sato", 2000)
sato.print_birth()
Sato was born in 2000

Personクラスcurrent_yearという属性を追加してください。deathで没年を受け取って、print_death()メソッドを追加する。

Personクラスage()年齢を計算するメソッドを追加してください。

Personクラスをインスタント化にしてくだい。その際、current_yearの値として2023を受け取って、年齢を計算してください。

親クラスからの継承#

継承は、既存のクラスをもとにして、変更部分だけを与えることにより、 新たなクラスを定義する機能です。

元のクラスは親クラスまたはスーパークラス、基底クラス、新しいクラスは子またはサブクラスと呼ばれます。

以下の例では、PersonクラスをもとにしてStudentクラスを定義しています。

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

    def say_hello(self):
        print("Hello, my name is", self.name)
    
    def print_birth(self):
        print("{} was born in {}".format(self.name,self.birth))
    
    def age(self):
        return self.current_year - self.birth
class Student(Person):
    pass # 何もしない

クラスは他のクラスを継承したものかどうかは、issubclass()を使えば調べられます。

issubclass(Student, Person)
True

次に、継承を使用し、新しいクラスを作成してみよう。

class Student(Person):
    def __init__(self, name, birth, number):
        super().__init__(name, birth)
        self.number = number

    def introduce(self):
        super().say_hello()
        print("I am a student.")

StudentクラスがPersonクラスを継承する際、__init__メソッドではsuper()を使用することにより、Personクラスの属性namebirthを継承できます。

  • super()が親クラスのPersonの定義を取り出す

  • super().__init__()メソッド呼び出しは、Person.__init__()メソッドを呼び出す

  • self.numberPersonクラスに含まれていなく、Studentクラスに新しい属性を追加する

john = Student("John", 2000, "S12345")
john.name
'John'
john.birth
2000
john.number
'S12345'

さらに、Studentクラスにはsay_helloメソッドなど親クラスであるPersonクラスで定義されたメソッドが追加されて、super().say_hello()という形でを呼び出されます。

john.say_hello()
Hello, my name is John
john.introduce()
Hello, my name is John
I am a student.
  • StudentクラスがPersonクラスを継承する際、current_yearという属性も継承してくだい。

  • Studentクラスにprint_age()というメソッドを追加してくだい。print_age()は、親クラスで定義されたage()メソッドで年齢を計算し、{name} is {age} years oldという形でプリントしてください。例えば、名前はsato, 年齢は23の場合、sato is 23 years oldが表示されるようにしてください。

集約#

Pythonにおけるクラスの集約(aggregation)とは、あるクラスが他のクラスのインスタンスをメンバーとして持つことを指します。つまり、1つのクラスが他のクラスを部品として利用し、それらのクラスの機能やデータを組み合わせて利用することができるということです。

クラスの集約は、関連性のあるオブジェクトを独立して定義し、それらを組み合わせてより複雑な構造を作成する場合に便利です。

class Address:
    def __init__(self, state, city, street):
        self.state = state
        self.city = city
        self.street = street

    def get_full_address(self):
        return f"{self.state} {self.city} {self.street}"
class Person:
    def __init__(self, name, birth, current_year=2023,address=None):
        self.name = name
        self.birth = birth
        self.current_year = current_year
        self.address = address

    def say_hello(self):
        print("Hello, my name is", self.name)
    
    def print_birth(self):
        print("{} was born in {}".format(self.name,self.birth))
    
    def age(self):
        return self.current_year - self.birth
    
    def get_person_info(self):
        address_info = self.address.get_full_address()
        return f"Name: {self.name}, Age: {self.age()}, Address: {address_info}"
# Addressのインスタンスを作成
address = Address("宮城県", "仙台市", "青葉区川内27番1号")

# Personのインスタンスを作成し、Addressのインスタンスを渡す
sato = Person(name="佐藤", birth=2000, address=address)
sato.get_person_info()
'Name: 佐藤, Age: 23, Address: 宮城県 仙台市 青葉区川内27番1号'
  1. Majorというクラスを作成し、専門分野に関する情報を保持する。

  2. Majorクラスには専攻名(major)、所属学部(department)、入学年(duration)が含まれている。

  3. StudentクラスがPersonクラスを継承する。その際、

  • Majorのインスタンスを渡す

  • 入学年(duration)と現在の年(current_year)で学年を計算するメソッドを定義する

  1. Studentクラスをインスタンス化して、学年を計算してください。

特殊メソッド#

  • num=1+9のようなコードを実行する際、値1と値9を持つ整数オブジェクトは、+が数学の足し算の意味で使われています

  • name='Karl'+'Marx'のようなコードを実行する際、+が文字列の結合の意味で使われています。

なぜPythonがどの使い方にすべきことを知るといえば、特殊メソッドで演算子の機能を定義しているわけです。

特殊メソッドは、クラスの振る舞いや組み込みの言語機能をカスタマイズするために使用されます。

メソッド名の前後に2つのアンダースコア(ダブルアンダースコア)が付いていることが特徴です。

既に紹介した__init()__は、初期化ための特殊メソッドとして知られており、オブジェクトが作成される際に自動的に呼び出されます。オブジェクトの初期化やインスタンス変数の設定など、初期化に必要な処理を実行します。

次のコードは、equal()という通常のメソッドを定義します。

このメソッドにより、文字列を小文字へ変換してから比較します。

class Word:
    def __init__(self, text):
        self.text = text

    def equal(self,word2):
        return self.text.lower() == word2.text.lower()
sendai=Word("Sendai")
sendai2=Word("sendai")
tokyo=Word("Tokyo")
sendai.equal(tokyo)
False
sendai.equal(sendai2)
True
sendai == sendai2
False

ここで、組み込み型と同じように、==を使いたいときは、equal()メソッドを__eq__という特殊メソッドに変更します。

class Word:
    def __init__(self, text):
        self.text = text

    def __eq__(self,word2):
        return self.text.lower() == word2.text.lower()
sendai=Word("Sendai")
sendai2=Word("sendai")
tokyo=Word("Tokyo")
sendai == sendai2
True
sendai == tokyo
False

メソッド名

説明

__eq__(self, other)

==

__ne__(self, other)

!=

__lt__(self, other)

<

__le__(self, other)

<=

__gt__(self, other)

>

__ge__(self, other)

>=

メソッド名

説明

__add__(self, other)

+

__sub__(self, other)

-

__mul__(self, other)

*

__truediv__(self, other)

/

__floordiv__(self, other)

//

__mod__(self, other)

%

__pow__(self, other)

**

メソッド名

説明

__str__(self)

オブジェクトの文字列表現を返す。str()

__repr__(self)

オブジェクトの公式な表現を返すために使用されます。repr()

__len__(self)

オブジェクトの長さ(要素数)を返すために使用されます。len()

class Word:
    def __init__(self, text):
        self.text = text
    def __eq__(self,word2):
        return self.text.lower() == word2.text.lower()
    def __repr__(self):
        return "The word is {}".format(self.text)
    def __str__(self):
        return self.text.upper()
sendai=Word("Sendai")
sendai
The word is Sendai
print(sendai)
SENDAI

クラスPersonに二人の年齢は等しいか、異なるか、より小さいか、より大きいか、を判断する演算を特殊メソッドで定義してください。