SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

よろずプログラマーのためのPython導入ガイド

Java meets Python - 第5回 二分木と辞書

よろずプログラマーのためのPython導入ガイド (7)

辞書:キーと値

 組み込み型dictに対して規定されたプロコトルを忠実に実現すると、二分木を辞書としても扱えるようになります。そこで、二分木を辞書に変身(?)させる過程を通して、相互の理解を深めましょう。

 その前に、以下の事例に共通する、辞書のインスタンスmを生成しておきます。

>>> m = dict([(e,ord(e)) for e in "ABC"])
>>> m
{'A': 65, 'C': 67, 'B': 66}

 辞書mには、文字列キーとその ASCII 値からなる要素対が含まれます。

メソッド keys を使うと

 メソッドkeysを利用すると、辞書に含まれるすべてのキーを列挙したリストが得られます。すると、次のように、辞書の中にキーとして登録された、すべての文字列を列挙したリストが得られます。

>>> m.keys()
['A', 'C', 'B']

 まず、二分木を表わすクラスBinTreeの傘下に、メソッドkeysに相当するものを実現します。クラスTnone/Tnodeの違いに注意しながら、読み進んでください。

class BinTree:
    tab = " "*6

class Tnone(BinTree):
    def keys(self):   return []

class Tnode(BinTree):
    def keys(self):
        node = [self.word]
        left  = self.left .keys()
        right = self.right.keys()
        return left + node + right

 クラスTnoneでは、左右の部分木を持たないので、単に空リスト [] をリターン値とします。クラスTnodeでは、二分木を構成する各ノードの中から、単語だけを抽出します。まず、変数nodeには、自身のノードに含まれる単語self.wordを設定します。次に、変数left,rightには、keys()によって得られる、左右の部分木に含まれる単語を列挙したリストを設定します。そして、これらのリストを連結したリストleft+node+rightをリターン値とします。

メソッド values を使うと

 メソッドvaluesを利用すると、辞書に含まれるすべての値を列挙したリストが得られます。すると、次のように、辞書の中に値として登録された、すべての整数値を列挙したリストが得られます。

>>> m.values()
[65, 67, 66]

メソッド items を使うと

 メソッドitemsを利用すると、辞書に含まれるすべてのキーと値を要素対とするタプルを列挙したリストが得られます。すると、次のように、辞書の中に要素対(キー/値)として登録された、すべてのタプル(文字列/整数値)を列挙したリストが得られます。

>>> m.items()
[('A', 65), ('C', 67), ('B', 66)]

 次に、先と同様にして、クラスBinTreeの傘下に、メソッドvalues/itemsに相当するものを実現します。

class Tnone(BinTree):
    def values(self): return []
    def items(self):  return []

class Tnode(BinTree):
    def values(self):
        node = [self.count]
        left  = self.left .values()
        right = self.right.values()
        return left + node + right
    def items(self):
        node = [(self.word, self.count)]
        left  = self.left .items()
        right = self.right.items()
        return left + node + right

 メソッドvalues/itemsについても同様です。クラスTnoneでは、左右の部分木を持たないので、単に空リスト [] をリターン値とします。クラスTnodeでは、二分木を構成する各ノードの中から、values()によって得られる、単語の出現頻度self.countだけを抽出して、これらを連結したリストをリターン値とします。同様に、items()によって得られる、単語とその出現頻度を要素対とするタプルself.word,self.countを列挙したリストをリターン値とします。

ためしてガッテン!

 では実際に、これらのメソッドの動作を確認してみましょう。

>>> p
good(1,as(2,,can(1,be(1,,),)),happy(2,,))
>>> p.keys()
['as', 'be', 'can', 'good', 'happy']

 メソッドkeysを利用すると、二分木に含まれる単語を列挙したリストが得られます。

>>> p.values()
[2, 1, 1, 1, 2]

 メソッドvaluesを利用すると、二分木に含まれる単語の出現頻度を列挙したリストが得られます。

>>> p.items()
[('as', 2), ('be', 1), ('can', 1), ('good', 1), ('happy', 2)]

 メソッドitemsを利用すると、単語とその出現頻度を要素対とするタプルを列挙したリストが得られます。

プロジェクトX(バツ):失敗から学べること

 話を進める前に、これらのメソッドkeys/values/itemsを実現する前まで、時計の針を戻してみましょう。過去にさかのぼって、その動作を確認してみるのも一興です。

>>> p
good(1,as(2,,can(1,be(1,,),)),happy(2,,))
>>> p.keys()
Traceback (innermost last):
...
AttributeError: 'instance' object has no attribute 'keys'
>>> p.values()
Traceback (innermost last):
...
AttributeError: 'instance' object has no attribute 'values'
>>> p.items()
Traceback (innermost last):
...
AttributeError: 'instance' object has no attribute 'items'

 どの場合も実行時に例外オブジェクトAttributeErrorを生成すると共に、エラーメッセージ 'instance' object has no attribute '...' を出力します。これは、属性としてのメソッドkeys/values/itemsをまだ実現していないことを表わします。

 洗練された OOP の世界では、「例外」さえもその例外ではなく「オブジェクト」として実現されます。つまり、ここでは例外オブジェクト(クラスAttributeErrorのインスタンス)を生成します。すると、例外オブジェクトから実行時のエラーに関する情報が得られるので、そこからメッセージを形成して、標準エラー出力に表示します。しかも、これらの作業はコンパイラーに頼らずに、自己責任で行います。Java と違って、Python の世界では「もう一人前 first-class object なのだから、親(コンパイラー)に頼らず自立しなさい」と言われ、一人前(熟練者)として扱われます。Python プログラマーに、若葉(初心者)マークは似合いません。

 プロジェクトX(エックス)から得られる感動は、明日への活力の源となりますが、それを達成するには「天から授かる」幸運も欠かせません。しかし、プロジェクトX(バツ)から得られる教訓は「自らの意思で」明日の失敗を未然に防ぐ術となります。成功よりむしろ失敗から学んだことの方が「経験に基づく知識」として役立つ場面も少なくありません。例外オブジェクトから、プログラマーが何を学べるかが肝要です。

どこで真偽を審議するのか

 組み込み関数boolを利用すると、指定された引数と等価な真偽値True/Falseが得られます。ここで、True/Falseは、クラスboolのインスタンスです。

>>> bool({})
False
>>> bool({"a":1})
True

 boolの引数に、空の辞書 {} を指定すると、Falseが得られます。空でない辞書を指定すると、Trueが得られます。つまり、辞書のインスタンスは「真偽値」としても扱えます。しかし、組み込み型と同様に、任意のクラスのインスタンスを真偽値として扱えるようにするためには、前もって準備が必要です。

メソッド __nonzero__ は何処に

 新たに定義したばかりのクラスのインスタンスは、まだ何も準備をしていないので、Trueと評価されます。ところが、メソッド__nonzero__を再定義すると、その真偽値を自由に再設定できます。

>>> class Foo: pass
>>> bool(Foo())
True

 メソッド__nonzero__を再定義しないと、新たなクラスFooのインスタンスは、Trueと評価されます。ところが、

>>> class Foo:
...     def __nonzero__(self): return False
>>> bool(Foo())
False

 このメソッドを再定義すると、Falseと評価されるのが分かります。

class Tnone(BinTree):
    def __nonzero__(self): return False

class Tnode(BinTree):
    def __nonzero__(self): return True

 メソッド__nonzero__を再定義するなら、クラスTnoneFalseと評価され、クラスTnodeTrueと評価されるようになります。

 では実際に、これらのメソッドの動作を確認してみましょう。

>>> p
good(1,as(2,,can(1,be(1,,),)),happy(2,,))
>>> bool(p)
True

 ここでは、二分木pは、部分木を持つので、Trueと評価されます。

>>> p = Tnone(); p
.
>>> bool(p)
False

 ここでは、二分木pは、部分木を持たないので、Falseと評価されます。

>>> p = Tnode(); p
(0,,)
>>> bool(p)
True

 ここでは、二分木pには、まだ単語とその出現頻度を登録していません。しかし、部分木を持つので、Trueと評価されます。

 このようにして、メソッド__nonzero__を再定義するだけで、その真偽値を自由に設定できます。すると、True/Falseと同様に、任意の二分木のインスタンスを、そのまま条件式として指定できるようになります。

《Tea Break》オブジェクトは考える葦です
 「誰が真偽値を決めるのですか」と問われたとき、SP を信仰するプログラマーは「それはコンパイラーです」と答えるでしょうし、OOP を信仰するプログラマーは「それはオブジェクト自身です」と答えるでしょう。もちろん、プログラマーにも、信仰の自由はあります。この噺のオチは、どちらの主張が正しいかではなく、立場の違いを象徴しているところです。メソッド __nonzero__ を再定義する意義は、クラス(主)がその配下のインスタンス(民)に対する責務を負うことです。これによって、コンパイラーに頼らずに、オブジェクト自身で「思考」できるようになります。
 プログラミング言語 Pascal がその名前にあやかったとされる、Blaise Pascal(1623-1662)は、彼が書き綴ったノートに「人間は考える葦である」と記しています。オブジェクトは、自ら考える意思を持つことで「一人前のオブジェクトfirst-classobject」として扱われ、OOP の世界で市民権を得ます。自他共に認める「一人前のプログラマー」となることを目指す皆さんなら、いつか、その意義を認めるときが訪れるでしょう。それが、コンパイラーの支配下から解放された、独立記念日(Independence Day)となるなら、その日が少しでも早く訪れることを願います。

次のページ
検索:イルカいるか?

この記事は参考になりましたか?

よろずプログラマーのためのPython導入ガイド連載記事一覧

もっと読む

この記事の著者

小泉ひよ子とタマゴ倶楽部(コイズミヒヨコトタマゴクラブ)

http://tamago-club.cocolog-nifty.com/「楽しくなければ仕事じゃない」が私たちのモットー。99%の苦悩の連続も、1%の成功に報われます。だからこそ、この仕事が楽しくて仕方がないのです。楽をするための努力なら惜しみません。何もせず楽をしているのと、努力をしたから楽ができるのと...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

本間 こりす(ホンマ コリス)

新人です。他のみなさんに支えられながら、マイペースで頑張ります。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1803 2008/04/04 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー