Pythonの辞書で擬似多次元配列
Pythonでは辞書のキーに tuple を使える。
>>> d = {(1,2,1):100, (2,2,2):200, (2,0,1):300} >>> d[(1,2,1)] 100
キーとしている tuple を三次元配列のインデクス(三次元空間の座標)とみなせば、d は三次元配列のように見える。
[ ] の中では tuple の ( ) は無くてもいいので省略すると、ちょっと辞書には見えない。なんか C# の多次元配列みたい。
>>> d[0,0,0] = 999 >>> d[1,1,1] = 777 >>> d[0,0,0] 999 >>> d[1,1,1] 777
あくまでも擬似的な物なので、
>>> d[1000000, 1000000, 1000000] = 1
とかやってもメモリが足りなくなることは無い。
辞書だもんね。
>>> len(d) 6 >>> for k, v in d.iteritems(): print k,v (1, 2, 1) 100 (1000000, 1000000, 1000000) 1 (2, 2, 2) 200 (0, 0, 0) 999 (2, 0, 1) 300 (1, 1, 1) 777
例えば、広大なN次元空間内でそれほど多くない座標を扱うような場合に、速度はともかくメモリ消費を抑えた(擬似)多次元配列として役立ちそう。
ところで1つ不満がある。
>>> d[0,1,2] Traceback (most recent call last): File "<pyshell#63>", line 1, in <module> d[0,1,2] KeyError: (0, 1, 2)
っぽく見えてても、ランダムにアクセスするとボロがでる。
まぁ、こうすればエラーは出ないけど。
>>> d.get((0,1,2), 0) 0
これじゃあ、せっかくの偽装がバレバレ。
で、クラス化してみる。
class MultiArray(dict): def __init__(self): super(MultiArray, self).__init__() def __getitem__(self, key): return self.get(key, 0)
必要最低限だけど。これだけも。
>>> ma = MultiArray() >>> ma[0,0,0] = 1 >>> ma[1,2,3] = 11 >>> ma[1,0,1] = 100 >>> ma[1,2,3] 11 >>> ma[1,1,1] 0 >>> ma[100,100,100] 0
それっぽい。
デフォルト値指定と次元数のチェックを追加してみた。
class MultiArray(dict): def __init__(self, **kw): super(MultiArray, self).__init__() self.dim = kw.get('dim', 2) self.default = kw.get('default', 0) def __getitem__(self, key): self._check_key(key) return self.get(key, self.default) def __setitem__(self, key, value): self._check_key(key) super(MultiArray, self).__setitem__(key, value) def _check_key(self, key): if len(key) != self.dim: raise IndexError()
三次元配列で、デフォルト値 -1 の場合はこんなん。
>>> ma = MultiArray(dim=3, default=-1) >>> ma[1,2,2] = 10 >>> ma[1,2,2] 10 >>> ma[0,0,0] -1 >>> ma[0,0,0] = 100 >>> ma[1,0,1] = 200 >>> ma[1,1,1] = 999 >>> g = ((x,y,z) for x in range(2) for y in range(2) for z in range(2)) >>> for key in g: print key, ma[key] (0, 0, 0) 100 (0, 0, 1) -1 (0, 1, 0) -1 (0, 1, 1) -1 (1, 0, 0) -1 (1, 0, 1) 200 (1, 1, 0) -1 (1, 1, 1) 999
次元数を間違えるとエラー
>>> ma[1,2] = 1 Traceback (most recent call last): (中略) IndexError
とりあえず期待通り動いた。
さらに改造するならインデクスの有効範囲とか……その前にインデクスの型チェックが必要?
チェックとか全部無くして、フリーダムな座標表現のままでも面白いけど。