2015年12月14日

with文の話2

with文での呼び出しを前提としたクラスの実装例を挙げます。

以下のクラスは、print(正確には sys.stdout)に送られてくる文字列を別 IO に取り込むクラスです。クラスは __enter__() と __exit__() のみから成り、with文で使われることが前提となっています。

import sys
import io

class PrintHack(object):
    def __enter__(self):
        # sys.stdoutをio.StringIOに置き換え
        self.__sio = io.StringIO()
        self.__tmp, sys.stdout = sys.stdout, self.__sio

        return self.__sio

    def __exit__(self, exc_type, exc_value, traceback):
        # sys.stdoutを元に戻す
        sys.stdout = self.__tmp

使い方は以下のようになります。
まず with文で PrintHack クラスを呼び出します。すると、with のスコープの中で呼ばれた print の内容は、標準出力には出力されず、PrintHack が持つ IO に取り込まれるようになります。取り込まれた内容は、IO の getvalue() で確認することができます。
このクラスの主な使い道は、テストプログラムにおいて標準出力の内容を確認する、となるはずです。
# with文でPrintHackを呼び出す
>>> with PrintHack() as hack:
...     print('Hello hack')
...
>>>   # ここではprintされない

# with内のprintの内容を確認
>>> hack.getvalue()
'Hello hack\n'

# with外では通常のprint
>>> print('Not hacked')
Not hacked

sys.stdout は文字通り標準の出力ですので、基本的にはデフォルトから変更を加えたくないものです。sys.stdout に加えた変更が間違って残ってしまうと、プログラムの他の箇所で予期せぬ問題が発生する恐れがあります。
with文専用のクラスを作ることで、影響範囲を with スコープの中に限定することができました。これは堅牢性の観点から非常に重要です。
また、仮にこのクラスが with文以外で呼ばれた場合、このクラスは何の動作もしません。with文以外では動作しないようにすることで、間違った使い方を未然に防ぐことができます。

0 件のコメント:

コメントを投稿