値の獲得
メソッドgetを利用すると、任意のキーに対応する値を参照できます。
>>> m
{'A': 65, 'C': 67, 'B': 66}
>>> m.get("A")
65
>>> m.get("@")
>>> print m.get("@")
None
引数に指定したキーが辞書に含まれるなら、それに対応する値が得られます。指定したキーが辞書に含まれないなら、その値が存在しないことを表わすNoneが得られます。ここでは、キー"A"は辞書mに含まれるので、対応する値 65 が得られます。また、キー"@"は辞書mに含まれないので、Noneが得られます。
ただし、対話モードでは、リターン値としてのNoneは出力されません。そこで、値を出力するために、ここではprint文を使っています。
class Tnode(BinTree):
def get(self, key):
value = None
node = self._detect(key)
if node: value = node.count
return value
メソッドgetでは、補助関数_detectを再利用しています。まず、リターン値を表わす変数valueには、初期値としてNoneを設定しておきます。次に、_detectを使って、指定した単語keyを含むノードnodeを獲得します。そして、ifに続く条件式nodeを満たすなら、該当するノードが存在するので、その単語の出現頻度node.countをリターン値valueとして設定します。
では実際に、これらのメソッドの動作を確認してみましょう。
>>> p
good(1,as(2,,can(1,be(1,,),)),happy(2,,))
>>> p.get("happy")
2
>>> p.get("god")
None
getの引数に指定した単語が、二分木に含まれるなら、それに対応する値が得られます。ここでは、単語"happy"は二分木pに含まれるので、その値 2 が得られます。また、単語"god"は二分木pに含まれないので、Noneが得られます。
値の参照(獲得)
演算子 [] を利用すると、任意のキーに対応する値を参照できます。
>>> m
{'A': 65, 'C': 67, 'B': 66}
>>> m["A"]
65
>>> m["@"]
Traceback (most recent call last):
...
KeyError: '@'
引数に指定したキーが辞書に含まれるなら、それに対応する値が得られます。指定したキーが辞書に含まれないなら、例外を生成します。ここでは、キー"A"は辞書mに含まれるので、対応する値 65 が得られます。また、キー"@"は辞書mに含まれないので、例外オブジェクトKeyErrorを生成すると共に、エラーメッセージとして、指定した単語'@'を出力します。
メソッド __getitem__ は何処に
メソッド__getitem__は、演算子 [] の操作を規定します。
class Tnone(BinTree):
def __getitem__(self, key): return None
class Tnode(BinTree):
def __getitem__(self, key):
node = self._detect(key)
if node: return node.count
raise KeyError, key
ここでは、補助関数_detectを再利用しています。まず、変数nodeには、_detectを使って得られる、指定した単語keyを含むノードを設定します。次に、ifに続く条件式nodeを満たすなら、該当するノードが存在するので、その単語の出現頻度node.countをリターン値とします。そして、条件式を満たさないなら、該当するノードが存在しないので、例外KeyErrorを生成すると共に、エラーメッセージとして、指定した単語keyを出力します。
では実際に、これらのメソッドの動作を確認してみましょう。
>>> p
good(1,as(2,,can(1,be(1,,),)),happy(2,,))
>>> p.tree()
+--
+-- 'as'(2)
+--
+-- 'be'(1)
+--
+-- 'can'(1)
+--
+-- 'good'(1)
+--
+-- 'happy'(2)
+--
>>> p["happy"]
2
>>> p["god"]
Traceback (innermost last):
...
KeyError: god
演算子 [] の中に指定した単語が、二分木に含まれるかどうかを判定した結果True/Falseが得られます。ここでは、単語"happy"は二分木pに含まれるので、その出現頻度 2 が得られます。また、単語"god"は二分木pに含まれないので、例外KeyErrorを生成すると共に、エラーメッセージとして指定した単語godを出力します。
値の設定(更新/追加)
演算子 [] を演算子 = と共に利用すると、キーに対応する値を設定できます。
>>> m
{'A': 65, 'C': 67, 'B': 66}
>>> m["C"] = hex(m["C"])
>>> m
{'A': 65, 'C': '0x43', 'B': 66}
>>> m["@"] = ord("@")
>>> m
{'A': 65, '@': 64, 'C': '0x43', 'B': 66}
引数に指定したキーが辞書に含まれるなら、それに対応する値を設定(更新)します。指定したキーが辞書に含まれないなら、新たなキーと値を設定(追加)します。ここでは、キー"C"は辞書mに含まれるので、対応する値'0x43'を設定します。また、キー"@"は辞書mに含まれないので、新たなキー"@"と値64を設定します。
メソッド __setitem__ は何処に
メソッド__setitem__は、演算子 [] の操作を規定します。
class Tnone(BinTree):
def __setitem__(self, key): return None
class Tnode(BinTree):
def __setitem__(self, key, value):
node = addtree(self, key)
node = node._detect(key)
if node: node.count = value
ここでは、補助関数_detectを再利用しています。まず、addtreeを使って、指定した単語keyを追加する対象となるノードを獲得して、そのノードを変数nodeに保持させます。次に、_detectを使って、指定した単語keyを含むノードnodeを獲得します。そして、ifに続く条件式nodeを満たすなら、該当するノードが存在するので、その単語の出現頻度node.countに値valueを設定します。
では実際に、これらのメソッドの動作を確認してみましょう。
>>> p good(1,as(2,,can(1,be(1,,),)),happy(2,,)) >>> p["happy"] = 3 >>> p good(1,as(2,,can(1,be(1,,),)),happy(3,,))
演算子 [] の中に指定した単語"happy"は、二分木pに含まれるので、その出現頻度を 3 に設定(更新)します。
>>> p["god"] = 4
>>> p
good(1,as(2,,can(1,be(1,,),god(4,,))),happy(3,,))
>>> p.tree()
+--
+-- 'as'(2)
+--
+-- 'be'(1)
+--
+-- 'can'(1)
+--
+-- 'god'(4)
+--
+-- 'good'(1)
+--
+-- 'happy'(3)
+--
演算子 [] の中に指定した単語"god"は、二分木pに含まれないので、新たにノードを生成します。そのノードには、単語"god"とその出現頻度 4 を設定(追加)します。
例外:AttributeError は語る
最後にもう一度、メソッド__getitem__/__setitem__を実現する前まで、話を戻します。
>>> p good(1,as(2,,can(1,be(1,,),)),happy(2,,)) >>> p["happy"] Traceback (innermost last): ... AttributeError: __getitem__ >>> p["happy"] = 3 Traceback (innermost last): ... AttributeError: __setitem__
実行時に例外オブジェクトAttributeErrorを生成すると共に、エラーメッセージとして、未定義のメソッドの名前__getitem__/__setitem__が出力されます。これらが、演算子 [] の操作を規定することは、その実行結果からも明らかです。
誰がために辞書はある
エラーメッセージが出力されると、初心者は、必要以上にプレッシャーを感じてしまいます。少しでも早く「その痕跡を消してしまいたい」という気持ちも理解できます。しかし、ときには「転んでもタダでは起きない」姿勢も必要です。
エラーメッセージを遠ざけるのではなく、むしろお友達として付き合うなら、良好な関係を築けるかもしれません。というのも、エラーメッセージは、迷子になりがちな開発の途上では、親切な水先案内人となるからです。次の表を見てください。

この対応表は、ある例外が発生したときに、その原因を探るための道標となります。左列には、クラスBinTreeの傘下で実現したメソッド群が並んでいます。中列には、各メソッドを実現するときに、その手本とした、組み込み型dictのプロトコルの一部を示しています。右列には、関連する例外オブジェクトの一覧があります。
例えば、__getitem__を再定義しないまま、演算子 [] を適用しようとすると、例外AttributeErrorを生成します。また、__getitem__を再定義しても、適切でないキーを指定すると、例外KeyErrorを生成します。前者は、プログラミング過程における想定外の出来事で、適切なプロセスを経る(メソッドを再定義)ように提供者を導きます。後者は、実行過程における想定内の出来事(仕様の一部)で、適切な使用法(存在するキーを指定)へと利用者を導きます。プログラマーには、提供者/利用者の二面性があるので、例外オブジェクトが語りかける警告に耳を傾けるのは重要です。
マトリックス型の組織では、裏マトリックスチームが、エラーメッセージを収集して、それを再利用可能なリソース(反面教師)として体系化します。裏という呼称は便宜的なもので、その活動はコインの裏表と似ています。ともすると「裏」という言葉に日陰者のイメージを連想しがちですが「影があるから輪郭が見えてくる」ものです。
エラーメッセージをキーにして、適切な値(対処法)が得られる逆引き辞書づくりのプロジェクトX(バツ)を、アプリケーション開発と並行して行う余裕があれば、いつか、プロジェクトX(エックス)の番組で、皆さんのプロジェクトが取り上げられる日が訪れるかもしれませんね。
ひよ子「ぴっ…」
参考文献
- 『鳩よ! 1991年3月号』 特集:谷川俊太郎 いるかいるか いないかいるか
