はじめに
今回はクラスを解説します。
YouTube動画
クラスの定義
クラスは既に解説したように「型」です。
組み込み型のみではなく、自前の型 (クラス) を定義出来ます。
以下の構文でクラスを定義出来ます。
class クラス名:
変数定義
メソッド定義
具体例です。
クラスのメソッドの第一引数には、self を使用します。
変数名は何でも良いのですが、慣例的に self を使用します。
>>> class Dog: # Dog クラスを定義
... def __init__(self, cnt): # コンストラクタを定義
... self.cnt = cnt
... def bark(self):
... print("ワンワン " * self.cnt)
...
>>> d = Dog(2) # Dog クラスのインスタンスを作成
>>> d.bark() # メソッドの呼び出し
ワンワン ワンワン
>> d.cnt # 属性を参照可能 (変更も可能)
2
>>> d # デフォルトのクラスの表示内容
<__main__.Dog object at 0x0000029D396B3610>
継承
既存のクラスに機能を追加するには、クラスの「継承」を使用します。
また、同じメソッド名で関数を再定義すると、再定義された方が呼び出されます。
これを「メソッドオーバーライド」と言います。
派生元のクラスを「基底クラス (ベースクラス)」、派生先のクラスを「派生クラス (サブクラス)」と言います。
派生クラスから基底クラスを呼び出すには、super() を使用します。
>>> class Dog:
... def __init__(self, cnt):
... self.cnt = cnt
... def bark(self):
... print("ワンワン " * self.cnt)
...
>>> class Chihauhau(Dog):
... def __init__(self, cnt, run_cnt): # コンストラクタをオーバーライド
... super().__init__(cnt) # 基底クラスのコンストラクタを呼び出し
... self.run_cnt = run_cnt
... def run(self): # 新たなメソッドを追加
... print("ラン " * self.run_cnt)
... def bark(self): # メソッドをオーバーライド
... print("キャンキャン " * self.cnt)
...
>>> c = Chihauhau(2, 3)
>>> c.run()
ラン ラン ラン
>>> c.bark() # 派生先のメソッドが呼び出される
キャンキャン キャンキャン
クラス変数とインスタンス変数
クラスの変数には、クラス変数とインスタンス変数があります。それぞれ以下の通りです。
- インスタンス変数: インスタンスに属する変数
- クラス変数: クラスに属する変数
>>> class Animal:
... word = "ガオー " # クラス変数
... def __init__(self, cnt):
... self.cnt = cnt # インスタンス変数
... def cry(self):
... print(Animal.word * self.cnt)
...
>>> a = Animal(2)
>>> b = Animal(3)
>>> a.cry()
ガオー ガオー
>>> b.cry()
ガオー ガオー ガオー
>>> Animal.word = "ブーブー " # クラス変数を書き換えると全てのインスタンスに影響が出る
>>> a.cry()
ブーブー ブーブー
>>> b.cry()
ブーブー ブーブー ブーブー
インスタンスメソッドとクラスメソッドと静的メソッド
クラスで定義可能なメソッドには、インスタンスメソッドとクラスメソッドと静的メソッドの 3 通りあります。それぞれ以下の通りです。
- インスタンスメソッド: インスタンスに属するメソッド (通常のメソッド)
- クラスメソッド: クラスに属するメソッド
- 静的メソッド: インスタンスにもクラスにも属さないメソッド (関数のようなもの)
クラスメソッドと静的メソッドは、インスタンスがなくても呼び出す事が出来ます。
また、クラスメソッドと静的メソッドは、クラスを継承した時に違いが出ます。
クラスメソッドは、継承先のクラスを参照出来ますが、静的メソッドは参照出来ません。
>>> class Animal:
... word = "ガオー "
... @classmethod
... def cry(cls):
... print(cls.word * 2)
... @staticmethod
... def cry_static():
... print(Animal.word * 2)
...
>>> class Sheep(Animal):
... word = "メェ "
...
>>> class Duck(Animal):
... word = "ガーガー "
...
>>> Sheep.cry() # Sheep クラスの word が参照される
メェ メェ
>>> Sheep.cry_static()
ガオー ガオー
>>> Duck.cry() # Duck クラスの word が参照される
ガーガー ガーガー
>>> Duck.cry_static()
ガオー ガオー
特殊メソッド
掛け算を表す演算子の「*
」を適用した時に、int 型のオブジェクトと str 型のオブジェクトで挙動が異なりました。
これは、演算子の挙動を表す特別なメソッドが実装されているからです。
これを「特殊メソッド」もしくは、「マジックメソッド」と言います。
「*
」の場合、__mul__という特殊メソッドが実装されています。
このメソッドを再定義 (オーバーロード) する事で、挙動を変更する事が出来ます。
>>> class MyStr(str):
... def __mul__(self, cnt): # 演算子「*」をオーバーロード
... return "[" * cnt + self.__str__() + "]" * cnt
...
>>> a ="abc"
>>> a * 3
'abcabcabc'
>>> b = MyStr("abc")
>>> b * 3 # MyStr の __mul__ が呼ばれる
'[[[abc]]]'
特殊メソッドは、他にも色々ありますが、ここでは省略します。
_str_と_repr_
例えば、bytes 型のように内部データとしては、「0 から 255 までの整数値の配列」なのに対して、print() 関数などで表示されるのは、「バイト列の文字列」です。
このように、「データ」と「表示」は異なる概念です。
クラスのオブジェクトの表示内容を実装する特殊メソッドが、__str__
と __repr__
です。
__str__
と __repr__
の違いは以下の通りです。
-
__str__
: ユーザが読みやすい文字列 -
__repr__
: eval() 関数などで元に戻せる文字列
__str__
呼び出しが必要な場面で、__str__
が実装されていない場合は、__repr__
が呼び出されます。
(逆に、__repr__
呼び出しが必要な場面で、__repr__
が実装されておらず、__str__
が実装されていても、__str__
は呼び出されず、デフォルトの文字列が取得されます)
>>> class BracketStr(str):
... def __str__(self):
... return "[" + super().__str__() + "]"
...
>>> s = BracketStr("abc")
>>> print(s) # __str__が呼ばれる
[abc]
>>> s.__str__()
'[abc]'
>>> s # __repr__が呼ばれる
'abc'
>>> s.__repr__()
"'abc'"
>>> eval(_)
'abc'
データクラス
クラスにメソッドは不要でデータのみ必要な場合に、Python 3.7 からデータクラスというものが用意されました。
以下の例のように使用します。
>>> from dataclasses import dataclass
>>>
>>> @dataclass
... class Person:
... name: str
... age: int
... gender: str = "Male" # デフォルト値を指定可能
...
>>> p = Person("PythonMan", 100)
>>> p
Person(name='PythonMan', age=100, gender='Male')
>>> p.name, p.age, p.gender
('PythonMan', 100, 'Male')
>>> p.__str__()
"Person(name='PythonMan', age=100, gender='Male')"
>>> p.__repr__()
"Person(name='PythonMan', age=100, gender='Male')"
他にも色々機能はありますので、他は公式ドキュメントをご参照下さい。
おわりに
以上、クラスに関して解説しました。
次回は、モジュールに関して解説します。