Python3の勉強(10) クラス

Python基礎勉強10日目です。今回は「クラス」について。

オブジェクト

Pythonでは数値、文字列からモジュールにいたるまで、何か具体的なインスタンスのことをオブジェクトと呼ぶ。

>>> num_val = 1         # 数値オブジェクト
>>> str_val = 'abc'     # 文字列オブジェクト
>>> def test_def():
...     print('hi')
... 
>>> def_val = test_def  # 関数オブジェクト

クラス

複雑なオブジェクトのひな型を作りたいことがある。
そんな時はクラスを使う。
とりあえず空クラスを作ってみる。

class Makohira():
    pass

このクラスを使ってオブジェクトを作ってみる。

>>> mako_obj1 = Makohira()
>>> mako_obj2 = Makohira()

オブジェクトの初期化時に動きを持たせてみる。
初期化時には__init__メソッドが呼ばれる。第1引数はselfにする必要があり、実行時にそのオブジェクト自身が渡される。

class Makohira():
    def __init__(self):
        print('オブジェクトを作ったよ。')

もう一度オブジェクトを作り直してみる。

>>> mako_obj1 = Makohira()
オブジェクトを作ったよ。
>>> mako_obj2 = Makohira()
オブジェクトを作ったよ。

このままだと何の役にも立たないのでMakohiraクラスに情報をもたせてみる。

class Makohira():
    def __init__(self, hp, attack, defense):
        self.hp = hp
        self.attack = attack
        self.defense = defense

オブジェクトを作ってみる。

>>> mako_obj1 = Makohira(10, 5, 3)
>>> mako_obj1.hp
10
>>> mako_obj1.attack
5
>>> mako_obj1.defense
3

>>> mako_obj2 = Makohira(15, 10, 8)
>>> mako_obj2.hp
15
>>> mako_obj2.attack
10
>>> mako_obj2.defense
8

メソッド(関数)を持たせてみる。

class Makohira():
    def __init__(self, hp, attack, defense):
        self.hp = hp
        self.attack = attack
        self.defense = defense
    def about_me(self):
        print('HP:', self.hp)
        print('攻撃力:', self.attack)
        print('防御力:', self.defense)

オブジェクトを作ってメソッドを使ってみる。

>>> mako_obj1 = Makohira(10, 5, 3)
>>> mako_obj1.about_me()
HP: 10
攻撃力: 5
防御力: 3

>>> mako_obj2 = Makohira(15, 10, 8)
>>> mako_obj2.about_me()
HP: 15
攻撃力: 10
防御力: 8

キーワード引数を使ってオブジェクトを作ってみる。

>>> mako_obj3 = Makohira(defense=7, hp=11, attack=25)
>>> mako_obj3.about_me()
HP: 11
攻撃力: 25
防御力: 7

辞書の入った変数の頭に**をつけて引数を渡すこともできる。(引数の名前と数が完全に一致している必要がある)

>>> params_dict = {'attack':30, 'defense':25, 'hp':100}
>>> mako_obj3 = Makohira(**params_dict)
>>> mako_obj3.about_me()
HP: 100
攻撃力: 30
防御力: 25

継承

あるクラスを拡張したいとき継承という仕組みを使うと、そのクラスの機能を全て持たせて追加部分や変更部分のみを定義した別クラスを作ることができる。

class サブクラス名(スーパークラス名):  

のように定義する。 実際にMakohiraクラスを継承したSuperMakohiraクラスを作ってみる。

class Makohira():
    def __init__(self, hp, attack, defense):
        self.hp = hp
        self.attack = attack
        self.defense = defense
    def about_me(self):
        print('HP:', self.hp)
        print('攻撃力:', self.attack)
        print('防御力:', self.defense)
class SuperMakohira(Makohira):
    def about_me(self):
        print('私はただのマコヒラではない!')

この例ではabout_me()メソッドの内容を書き換えた。(これをオーバーライドという)
オブジェクトを作ってみる。

>>> super_mako_obj = SuperMakohira(100, 150, 178)
>>> super_mako_obj.about_me()
私はただのマコヒラではない!

super()を使うと、サブクラスからスーパクラスのメソッドを呼び出すことができる。

class SuperMakohira(Makohira):
    def about_me(self):
        super().about_me()
        print('私はただのマコヒラではない!')

オブジェクトを作ってみる。

>>> super_mako_obj = SuperMakohira(100, 150, 178)
>>> super_mako_obj.about_me()
HP: 100
攻撃力: 150
防御力: 178
私はただのマコヒラではない!

新しいメソッドを追加することもできる。

class SuperMakohira(Makohira):
    def about_me(self):
        super().about_me()
        print('私はただのマコヒラではない!')
    def say_hello(self):
        print('Hello!')

追加メソッドを使ってみる。

>>> super_mako_obj = SuperMakohira(100, 150, 178)
>>> super_mako_obj.say_hello()
Hello!

情報を追加してみる。

class SuperMakohira(Makohira):
    def __init__(self, hp, attack, defense, mp):
        super().__init__(hp, attack, defense)
        self.mp = mp
    def about_me(self):
        super().about_me()
        print('MP:', self.mp)

オブジェクトを作ってみる。

>>> super_mako_obj = SuperMakohira(100, 150, 178, 50)
>>> super_mako_obj.about_me()
HP: 100
攻撃力: 150
防御力: 178
MP: 50

プロパティ

オブジェクトの属性に直接アクセスされるのを避けたい時に、プロパティが利用できる。
プロパティの作り方には2通りの方法がある。

1. 「プロパティー名 = property(ゲッター名, セッター名)」と書く方法

2. 次のようなデコレータを使う方法
@property
def プロパティ名(self):
    ゲッター定義
@プロパティ名.setter
def セッター名(self, input_value):
    セッター定義

実際にプロパティを定義してみる。
属性に__のようにアンダースコア2つで始まる名前を使うと外部に公開する名前をくちゃくちゃに変えてくれる。(完全に隠蔽されるわけではない)

class Makohira():
    def __init__(self, hp):
        self.__hp = hp
    def get_hp(self):
        return(self.__hp)
    def set_hp(self, input_hp):
        if (input_hp > 999):
            raise Exception('HPが999を超えているよ!!')
        self.__hp = input_hp
    hp = property(get_hp, set_hp)

動きを確認してみる。

>>> mako_obj = Makohira(10)
>>> mako_obj.hp
10
>>> mako_obj.hp = 500
>>> mako_obj.hp
500
>>> mako_obj.hp = 1000
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in set_hp
Exception: HPが999を超えているよ!!

クラスメソッド

オブジェクトではなくクラスにメソッドを持たせたい場合は@classmethodというでコレータを使う。第1引数にクラスが渡される。

class Makohira():
    count = 0
    def __init__(self):
        Makohira.count += 1
    @classmethod
    def how_many_makohira(cls):
        print(cls.count, "のMakohiraオブジェクトが作られています。")
>>> mako_obj1 = Makohira()
>>> mako_obj2 = Makohira()
>>> Makohira.how_many_makohira()
2 のMakohiraオブジェクトが作られています。

静的メソッド

オブジェクトにもクラスにも影響を与えることはないがクラスの中に定義したいメソッドを作りたい場合は@staticmethodデコレータを付けた静的メソッドを作ると良い。
第1引数にはselfもclsも必要ない。

class Makohira():
    @staticmethod
    def something():
        print('Live as if you were to die tomorrow. Learn as if you were to live forever.')
>>> Makohira.something()
Live as if you were to die tomorrow. Learn as if you were to live forever.

特殊メソッド

__で囲まれた名前のメソッドは特殊メソッドと呼ばれ特殊な動きを実装できる。
例えば+や-等の演算子の動きを実装できる。

class Makohira():
    def __init__(self, account_balance):
        self.account_balance = account_balance
    def __add__(self, mako_obj2):
        return Makohira(self.account_balance + mako_obj2.account_balance)
>>> mako_obj1 = Makohira(1000)
>>> mako_obj2 = Makohira(10000)
>>> mako_obj3 = mako_obj1 + mako_obj2
>>> mako_obj3.account_balance
11000

他にも色々な特殊メソッドが利用できる。

比較
__eq__(self, other) : 「self = other」
__ne__(self, other) : 「self != other」
__lt__(self, other) : 「self < other 」
__gt__(self, other) : 「self > other」
__le__(self, other) : 「self <= other」
__ge__(self, other) : 「self >= other」

算術
__add__(self, other)        : self + other
__sub__(self, other)        : self - other
__mul__(self, other)        : self * other
__floordiv__(self, other)   : self // other
__truediv__(self, other)    : self / other
__mod__(self, other)        : self % other
__pow__(self, other)        : self ** other

その他(全てではない)
__str__(self)   : str(self)    : print関数等で使われる
__repr__(self)  : repr(self)   : 対話型インタープリタの表示で使われる。
__len__(self)   : len(self)

コンポジション

クラスを使った設計をするときに継承(is-a関係)ではなくコンポジション(has-a関係)を使った方が良い場合が多々ある。

class Tire():
    def __init__(self, size):
        self.size = size

class Body():
    def __init__(self, color):
        self.color = color

class Car():
    def __init__(self, tire, body):
        self.tire = tire
        self.body = body
>>> small_tire = Tire(10)
>>> yellow_body = Body('yellow')
>>> car1 = (small_tire, yellow_body)

>>> large_tire = Tire(20)
>>> blue_body = Body('blue')
>>> car2 = (large_tire, blue_body)

勉強に使っている本はオライリーの「入門Python3」


やり直しPython3(モジュール、パッケージ)

2016-05-06(Fri) by Makoto Yamahira

Python3のやり直しメモ(モジュール、パッケージ)

read more

やり直しPython3(try, except)

2016-04-30(Sat) by Makoto Yamahira

Python3のやり直しメモ(try, except)

read more

やり直しPython3(内包表記)

2016-04-22(Fri) by Makoto Yamahira

Python3のやり直しメモ(内包表記)

read more

やり直しPython3(関数)

2016-04-22(Fri) by Makoto Yamahira

Python3のやり直しメモ(関数)

read more

やり直しPython3(if文、 while文、for文)

2016-04-18(Mon) by Makoto Yamahira

Python3のやり直しメモ(if文、while文、for文)

read more

やり直しPython3(言語仕様基礎)

2016-04-17(Sun) by Makoto Yamahira

Python3のやり直しメモ(言語仕様基礎)

read more

やり直しPython3(リスト、タプル、辞書、集合)

2016-04-04(Mon) by Makoto Yamahira

Python3のやり直しメモ(リスト、タプル、辞書、集合)

read more

やり直しPython3(文字列)

2016-03-30(Wed) by Makoto Yamahira

Python3のやり直しメモ(文字列)

read more

やり直しPython3(数値、変数)

2016-03-26(Sat) by Makoto Yamahira

Python3のやり直しメモ(変数、数値)

read more