背景.
例えば、「テーブルはレコードのリストであり、レコードは辞書(連想配列)である」(=テーブルとは辞書のリストである)という考え方に基づいて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__
とは別物だから循環参照にならない」というわけだ。