その他の特殊メソッド
クラスを定義してインスタンスオブジェクトを生成するときには、はじめに__init__()メソッドがコンストラクタとして呼び出されることは説明しました。
この他にあるタイミングで呼び出される特殊メソッドがあります。
__str__() と __repr__() メソッド
組み込み関数 str(object) と repr(object) が実行されたときに呼び出されるメソッドです。
例を見てみる方が理解が簡単だと思います。
code: 0715_class_magic_methods_str_and_repr.py
class Artist():
def __init__(self, firstname='', lastname=''):
self.firstname = firstname
self.lastname = lastname
def __repr__(self):
return( f'repr {self.firstname}_{self.lastname}')
def __str__(self):
return( f'str {self.firstname}_{self.lastname}')
def show_name(self):
return( f'{self.firstname}_{self.lastname}')
queen = Artist('Freddie', 'Mercury')
print(queen.show_name())
print(queen)
print(str(queen))
print(repr(queen))
組み込み関数repr(object) はobject.__repr__() を呼び出して、その文字列を返します。同様に、組み込み関数str(object) はobject.__str__() を呼び出して、その文字列を返します。このとき、__str__() が定義されていないときは、__repr__() を呼び出します。
__str__(): 人が読みやすい形式で出力をすればよい。自由度が高い。
__repr__(): eval() で元のオブジェクトに戻せるように定義する(ことが望ましい)
print(object) は object.__str__() を呼び出しています。
eval() は引数で与えられた文字列をPythonの式として実行する関数です。
repr() は representation 、str() は strings、eval() は evaluate を略した関数名です
意味を理解すると覚えやすいでしょう。
__call__()メソッド
__call__()メソッドについても説明より動作を確認した方が理解が早くなります。
code: 0712_class_magic_method_call.py
class MyClass:
def __init__(self, arg1, arg2, arg3):
self.var1 = arg1
self.var2 = arg2
self.var3 = arg3
def __call__(self, *args):
self.var1, self.var2 = args
obj = MyClass(1, 2, 3)
print(obj.__dict__)
print(id(obj))
print(callable(obj))
obj(20,30)
print(obj.__dict__)
print(id(obj))
オブジェクトのアトリビュートの状態は__dict__を参照すると知ることができます。
組み込み関数 id() はオブジェクトIDを返すものです。
MyClassクラスはコンストラクタとして、引数を3つ受け取る__init__()を持っています。インスタンスオブジェクトobjの内部変数は、生成時には次のようになります。
self.var1: 1
self.var2: 2
self.var3: 3
この内部変数を変更したいときには、通常は、その変数を更新するメソッドを定義する必要があります。
こうしたときに特殊メソッド__call__()を使うと、少し簡潔に定義することができます。
obj(20, 30) が実行されると、特殊メソッドobj.__call__(20, 30)が呼び出されて、
内部変数を置き換えることができます。
self.var1: 20
self.var2: 30
self.var3: 3
この例のobj(20, 30) のように、オブジェクト自身を関数のように呼び出せるオブジェクトを呼び出し可能オブジェクト(Callable Object) といいます。
組み込み関数 callable() を使うと、オブジェクトが呼び出し可能かどうかを調べることができます。
シーケンスオブジェクトでの特殊メソッド
Pythonで独自に作成したクラスを組み込みシーケンス(dict、tuple、list、strなど)のように動作させるためには、次の特殊メソッドを定義する必要があります。
__len __(self): コンテナの長さを返す、len()はこの特殊メソッドを呼び出す
__getitem __(self, key): アイテムにアクセスするときの動作を定義する
__setitem __(self, key, value): アイテムが割り当てられるときの動作を定義する
__delitem __(self, key): アイテムが削除されたときの動作を定義する
__iter __(self) :コンテナのイテレータを返す
__reversed __(self): 組み込み関数 reversed()で呼び出される動作を実装する
__contains __(self, item): in や not での動作を定義する
__missing __(self, key): 辞書に存在しないキーにアクセスした時の動作を定義する
次の例は、リストオブジェクトを実装するためのテンプレートです。
code: 0713_class_magic_methods_for_list.py
class MyList:
def __init__(self, values=None):
if values is None:
self.values = []
else:
self.values = values
def __len__(self):
return len(self.values)
def __getitem__(self, key):
def __setitem__(self, key, value):
def __delitem__(self, key):
def __iter__(self):
return iter(self.values)
def __reversed__(self):
return reversed(self.values)
def append(self, value):
self.values.append(value)
def head(self):
def tail(self):
def init(self):
def last(self):
def drop(self, n):
def take(self, n):
イテラルオブジェクトのクラス実装とジェネレータ実装
次の関数は引数で与えた整数が素数かどうかを判断するものです。
code: check_prime.py
def check_prime(number):
for divisor in range(2, int(number ** 0.5) + 1):
if number % divisor == 0:
return False
return True
初期化時に与えた個数の素数をイテラルオブジェクトで返すクラスPrimes は次のようになります。
code: 0714_class_prime_as_iterator.py
from check_prime import check_prime
class Primes:
def __init__(self, max):
self.max = max
self.number = 1
def __iter__(self):
return self
def __next__(self):
self.number += 1
if self.number >= self.max:
raise StopIteration
elif check_prime(self.number):
return self.number
else:
return self.__next__()
primes = Primes(100)
for x in primes:
print(x)
イテラルオブジェクトには__iter__() と __next__() を定義する必要がありますね。
このPrimesクラスの処理をジェネレータで実装すると次のように簡潔なものとなります。
code: 0715_class_prime_as_generator.py
from check_prime import check_prime
def Primes(max):
number = 1
while number < max:
number += 1
if check_prime(number):
yield number
primes = Primes(100)
for x in primes:
print(x)
クラスはコードの再利用と容易な拡張を実現します。
ただし、クラスに囚われすぎずに視野を広くすることも重要です。