時々、dict風の動作をする自前クラスを作りたくなることがあるが、何を満たせばdict風になるのか毎回調べていたので、ここにメモしておく。 と言っても、ドキュメントに全てまとまっているので、以下を参照するだけ。
抽象基底クラスについては、PEP 3119で提案がある。
ここで、以下のサンプルコードを実行してdict
とMutableMapping
のクラス階層を表示してみる。
import collections.abc from typing import * def print_class_hierarchy(t: Type, depth: int = 0): print(''.join(' ' for _ in range(depth)) + t.__name__ + ' (' + t.__class__.__name__ + ')') if t != object: for base_type in t.__bases__: print_class_hierarchy(base_type, depth+1) print_class_hierarchy(dict) print() print_class_hierarchy(collections.abc.MutableMapping) print() print(issubclass(dict, collections.abc.MutableMapping))
出力結果は以下のようになる。
dict (type) object (type) MutableMapping (ABCMeta) Mapping (ABCMeta) Collection (ABCMeta) Sized (ABCMeta) object (type) Iterable (ABCMeta) object (type) Container (ABCMeta) object (type) True
出力結果より、dict
とMutableMapping
はクラス階層的には継承関係にはないことが読み取れるが、issubclass(dict, MutableMapping)
はTrue
になる。
これは、PEP 3119の提案によると、issubclass()
をオーバーライドして、あたかもdict
がMutableMapping
のサブクラスであるかのように見せかけているためである。
言語のコアな部分を変更せずに、コレクションの性質を抽象化する工夫と思われる。
最後に蛇足だが、もうちょっと広くPythonのデータモデル (というより型モデル) はこちらを参照。
2013/10/15追記。 各クラスのメソッドも表示するようにして、継承関係のツリーの罫線も引いてみた。
import inspect import collections.abc from typing import * def print_class_hierarchy(node: Union[Type, str], indent_types: list[str] = []) -> None: indents = '' for index, indent_type in enumerate(indent_types): indent = '??????' # this value must be never used if index != len(indent_types) - 1: if indent_type == 'TYPE': indent = ' | ' elif indent_type == 'TYPE_LAST': indent = ' ' else: if indent_type == 'METHOD': indent = ' | ' elif indent_type == 'METHOD_LAST': indent = ' ' elif indent_type == 'TYPE': indent = ' +-- ' elif indent_type == 'TYPE_LAST': indent = ' `-- ' indents = indents + indent node_text = (node.__name__ + ' (' + node.__class__.__name__ + ')') if isinstance(node, Type) else node print(indents + node_text) if isinstance(node, Type): base_types = list(node.__bases__) methods = {member for member, _ in inspect.getmembers(node, inspect.isfunction)} base_methods = {member for base_class in node.__bases__ for member, _ in inspect.getmembers(base_class, inspect.isfunction)} indent_type = 'METHOD' if len(base_types) > 0 else 'METHOD_LAST' for method in list(methods - base_methods): print_class_hierarchy(method, indent_types + [indent_type]) if node != object: for index, base_type in enumerate(base_types): indent_type = 'TYPE' if index != len(base_types) - 1 else 'TYPE_LAST' print_class_hierarchy(base_type, indent_types + [indent_type]) print_class_hierarchy(collections.abc.MutableMapping)
出力結果は以下。
MutableMapping (ABCMeta) | setdefault | __setitem__ | pop | clear | popitem | __delitem__ | update `-- Mapping (ABCMeta) | items | get | values | keys | __eq__ | __getitem__ `-- Collection (ABCMeta) +-- Sized (ABCMeta) | | __len__ | `-- object (type) +-- Iterable (ABCMeta) | | __iter__ | `-- object (type) `-- Container (ABCMeta) | __contains__ `-- object (type)