背景.
例えば、「テーブルはレコードのリストであり、レコードは辞書(連想配列)である」(=テーブルとは辞書のリストである)という考え方に基づいてTable型を作るとしよう。
この考え方に基づいて早速
class Table(list):
def __init__(self, columns):
self.__columns = columns
def to_excel(self): ...
def to_pd_df(self): ...
def to_numpy(self): ...
@classmethod
def from_excel(Table, excel_path): ...
@classmethod
def from_pd_df(Table, df): ...
@classmethod
def from_numpy(Table, arr2d): ...
と書いたとする。
後はlistと同じように辞書をappend, clear, pop等、リストに対するすべての操作が利用可能である。
(なのでリスト内法表記などを用いてテーブルを自由自在にフィルタ出来るというわけだ)
ところで、append, += など、任意の操作をした際に、「中身がすべて辞書dであって、かつset(d.keys())がset(self.__columns)と一致するか」の検査を行いたいとしよう。
この場合はどうしたらよいか。
解決.
class Table(list):
def __getattribute__(self, method_name):
if hasattr(list, method_name): # 親クラスで定義されたメソッドの場合
def closure(*a, **ka):
# 親メソッド呼び出し前の処理をここにかく
rtn = object.__getattribute__(self, method_name) # 親メソッド呼び出し
self.__raise_error_if_illegal_component_found() # 親メソッド呼び出し後の処理をここに書く
return rtn
return closure
# 親クラスにないメソッドの場合
return object.__getattribute__(self, method_name)
def ...
__getattr__の場合は「存在しないメンバを参照されたときに動く」のに対し、
__getattribute__は「メンバの存在の有無にかかわらず直ちに呼び出される」強力なものとなっている。
そのため、__getattribute__内でself.hogeのように自オブジェクトのメンバを呼ぼうものなら、(再帰処理などを意図して書いた者でない限り)無限ループに陥ってしまう。
この無限ループを回避するために、object.__getattribute__を利用する。
「自クラスの__getattribute__とは別物だから循環参照にならない」というわけだ。