126:コレクションのような性質を持つクラスを作る
特殊メソッドを実装
自作のクラスに __getitem__(self, key) メソッドを定義すると、 組み込みのdict や list の様に myclass[egg]? で値を取り出せます。
class AnyIs1(object):
def __getitem__(self, key):
return 1
a = AnyIs1()
print a[0] # => 1
print a["aaaa"] # => 1
print a[range(10)] # => 1
次のような特殊メソッドがあります
詳細については リファレンスマニュアル 3.4 特殊メソッド名 を参照してください。
既存のコレクションに委譲する
コレクション風のクラスを作るときは、0から作るのではなく、 既存のコレクションを利用するのが一つの方法です。
class MultiDict(object):
""" 辞書風のコレクション。一つのキーに複数の値を持つ。 """
def __init__(self):
self._dict = {} # <= 既存の辞書を属性に持つ
def __setitem__(self, key, value):
t = self._dict.get(key, ())
self._dict[key] = t + (value,)
# 新たに定義するのは __setitem__のみ
# 他は全て self._dictに委譲するだけ
def __getitem__(self, key):
return self._dict[key]
def __delitem__(self, key):
del self._dict[key]
def __iter__(self):
return iter(self._dict)
def __len__(self):
return len(self._dict)
def __contains__(self, item):
return item in self._dict
def __str__(self):
return str(self._dict)
m = MultiDict()
m[1] = 1
m[1] = 2
m[1] = 3
m[2] = 5
m[2] = 4
m[2] = 3
print(1, m[1]) # => 1 (1, 2, 3)
print(2, m[2]) # => 2 (5, 4, 3)
print(m) # => {1: (1, 2, 3), 2: (5, 4, 3)}
collections のクラスを継承する
特殊メソッドで定義されるのは、コレクションとしては最低限の機能です。 たとえば、組み込みdictには他にも .iteritems() .update() といったメソッドもあります。
もちろん、.iteritems() .update() を自分で定義する事も出来ます。 しかし、collectionsの抽象基底クラスを継承すると、 これらのメソッドを自動的に追加する事ができます。
class MultiDict(MutableMapping):
""" 辞書風のコレクション。一つのキーに複数の値を持つ。 """
def __init__(self):
self._dict = {} # <= 既存の辞書を属性に持つ
def __setitem__(self, key, value):
t = self._dict.get(key, ())
self._dict[key] = t + (value,)
def __getitem__(self, key):
return self._dict[key]
def __delitem__(self, key):
del self._dict[key]
def __iter__(self):
return iter(self._dict)
def __len__(self):
return len(self._dict)
def __contains__(self, item):
return item in self._dict
def __str__(self):
return str(self._dict)
m = MultiDict()
m[1] = 1
m[1] = 2
m[1] = 3
m[2] = 1
print m.keys() #=> [1, 2]
print m.items() #=> [(1, (1, 2, 3)), (2, (1,))]
MutableMapping? は dict風のクラスを作るための抽象基底クラスです。 他にもlist風のMutableSequence、set風のMutableSetがあります。 詳しくは`Python documentation collections`をご覧ください。
複数の抽象基底クラスを継承する事もできます。
class FrozenMap(Mapping, Hashable):
"""変更不可能、辞書の様に値を取り出せて、辞書のキーに使用できる"""
def __init__(self, adict):
self._dict = dict(adict)
self._tuple = tuple(adict.iteritems())
def __getitem__(self, key):
return self._dict[key]
def __iter__(self):
return iter(self._dict)
def __len__(self):
return len(self._dict)
def __eq__(self, other):
return self._tuple == other._tuple
def __hash__(self):
return hash(self._tuple)
残念ながら、これらの抽象基底クラスは、Python 2.5以前では使えません。 2.5以前では、代わりにUserDict、UserListなどのモジュールを使用します。 詳しくは`ライブラリリファレンス 5.12 UserDict?`、 ライブラリリファレンス 5.13 UserList? をご覧ください。
組み込みのコレクションを継承する
組み込みのコレクションを継承して、新たなコレクションを作る事もできます。
class MultiDict(dict):
"""一つのキーに複数の値 (辞書を継承)"""
def __setitem__(self, key, value):
t = dict.get(self, key, ())
dict.__setitem__(self, key, t + (value,))