《18》クラス(4)eval〈Python 2.x 版〉

実録:はじめてのプログラミング記事一覧
《18》クラス(4)eval

《著》小粒ちゃん+α《監修》小泉ひよ子とタマゴ倶楽部
2009年2月6日(金)

今日の進捗

  • Tutorial: Whetting Your Appetite
  • Python.use(better) -- セミナー研修テキスト
  • 逆ポーランド課題を「続・ひよ子のきもち」で公開
Comment
本人:野中 4週間ありがとうございました。福岡に帰ってからもよろしくお願いします。
担当:本間 こちらこそ、どうもお疲れさまでした。福岡に行くことがあったら、そのときはよろしくね。(^^)

逆ポーランド課題(2)eval

リファクタリングを継続する前に、途中経過をまとめます。


class Polish(object):                # ver.2
    def __init__(self):
        self.stack = Stack()
    def eval(self, expression):
        parser = Parser()
        for e in expression.split():
            parser.scan(e).eval(self)
        return self.stack.pop()            
    def eval_Value(self, value):
        self.stack.push(value)
    def eval_Op(self, value):
        op2 = self.stack.pop()
        op1 = self.stack.pop()
        self.stack.push(eval("%s %s %s"%(op1, value, op2)))

《Note》始めは「このコードの断片は分かりにくい」と感じるかもしれません。やがて、オブジェクト指向プログラミング〔Object-Oriented Programming; OOP〕が理解できるようになると、むしろ「このほうが分かりやすい」と感じるようになり、それこそが「OOP を習得できた」何よりの証です。《ひよ子》

parser が抽出した各トークンは、それが演算子かどうかで個別の処理が必要です。ところが、

        for e in expression.split():
            parser.scan(e).eval(self)

このコードの断片には、具象クラス Value/Op が含まれていません。ここで注目して欲しいのは、具体的な記述を含まない「抽象的な表現」になっていることです。つまり、なにか仕様変更があっても、このコードの断片を更新する必要がないのです。

《Note》逆に、具体的な記述を含むと、仕様変更のたびに「モジュールを開かないと」コードを更新できなくなります。次の《Tips》を参照してください。《りす》

class Token(object):                # ver.2a
    ...
    def eval(self, client):
        eval("client.eval_%s(self)"%self.__class__.__name__)

ver.1a/ver.1b で示したコードの断片は「メソッドの名前」が違うだけで、それ以外は同じです。そこで、リファクタリングを実践して、その共通する箇所を抽象クラス Token に記述します。すると、

class Value(Token): pass
class Op   (Token): pass

クラス Value/Op は、その存在に意義があるだけなので、その本体は空 pass になります。

《Note》pass を記述する(何も書かない)ことに重要な意味があります。これは、構文として必要だから記述するのとは、意味が違います。《りす》

Tips

ver.1 で示したコードの断片は「具体的で分かりやすい」という長所はあっても、逆に「具体的…」であることが短所にもなり得ます。なぜなら、要求仕様の変更があるたびに更新する必要があり、このクラス Polish を含む .py モジュールを「永遠に」閉じることができないからです。つまり、開放閉鎖原則〔Open-Closed Principle; OCP〕にも背くことになります。《ひよ子》

Last updated♪09/03/10