Python.use(better, follow=”K&R”) リファクタリング 9/10
Python.use(better, follow=”K&R”) # for novice 《記事一覧》
[Python]メソッド keys/values/items
組み込み型 dict には、次の3つのメソッドを適用できます。クラス eXdict の傘下でも、これに準拠します。
- メソッド keys を利用すると「キー」を列挙したリストが得られます。
- メソッド values を利用すると「値」を列挙したリストが得られます。
- メソッド items を利用すると、要素対(キー/値)からなる「タプル」を列挙したリストが得られます。
[Python]何が問題か?
# cz_08.py -------------------------------------------- before ---
class Tnode(BinTree):
def keys(self):
node = [self.word]
left = self.left .keys()
right = self.right.keys()
return left + node + right
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
3つのメソッド keys/values/items の本体を見ると、その違いは変数 node に保持する情報と、再帰的なメソッド呼び出しだけだと分かります。そこで、これらの異なるコードの断片から共通する構造を抽出して、それを再利用可能にするために、リファクタリングを履行します。
[Python]ある解決法
class Tnode(BinTree):
def _do(self, func):
node = [eval("self._%s()"%func)]
left = eval("self.left .%s()"%func)
right = eval("self.right.%s()"%func)
return left + node + right
まず、引数 func が参照する関数オブジェクトごとに、異なる情報 node を得ます。次に、左右の部分木 left/right に対しては、引数 func が参照する関数オブジェクトに、その後処理を委ねます。そして、これらを連結したリスト left+node+right をリターン値とします。
def _keys (self): return self.word
メソッド keys に固有の処理を記述します。ここでは、単語 self.word をリターン値とします。
def _values(self): return self.count
メソッド values に固有の処理を記述します。ここでは、出現頻度 self.count をリターン値とします。
def _items (self): return self.word, self.count
メソッド items に固有の処理を記述します。ここでは、単語/出現頻度を要素対とするタプル self.word,self.count をリターン値とします。
共通する処理(テンプレート)_do と、メソッドごとに固有の処理 _keys/_values/_items とを分割統治したので、コードの見通しが良くなり、その保守も容易になります。
# cz_08.py -------------------------------------------- after ---
class eXdict:
def keys (self): return self._do("keys")
def values(self): return self._do("values")
def items (self): return self._do("items")def _do(self, func): raise NotImplementedError
リファクタリング後のメソッド keys/values/items では、前述した補助関数 _do の引数に、各処理を識別する "keys"/"values"/"items" を指定するだけです。
抽象クラス eXdict では、メソッド _do が、例外 NotImplementedError を生成します。これは、子孫クラスで、このメソッドを再定義しないときに、プログラマーの注意を喚起します。