ContextDecorator

Python学習【365日チャレンジ!】266日目のマスターU(@Udemy11)です。

iPhone12が発売されてすでに10日以上たちましたが、全くもって情報が入ってきません。

というか、情報を収集しようとしていないので、情報がはいってこないのは当たり前ですよね。

iPhone10くらいまでは、きちんと理解できないにもかかわらず、AppleのiPhone発表イベントをリアルタイムで視聴していた時がありましたが、年のせいか

問題なく使えてるからまーいいや

状態になってしまっている感はいなめません。

Facebook疲れならぬiPhone疲れになってしまったのでしょうか?

なんてことをいいつつ、今日もPython学習を始めましょう。

昨日の復習

昨日は、デコレーターの引数をスマートに活用できるcontextlib.contextmanagerを学習しました。

contextmanagerを使うことで、デコレーターの引数を使う際にコードを短縮することができました。

また、デコレーターではなく、withステートメントを使ってさらに短いコードにすることができたり、withステートメントをネストさせて使うこともできました。

くわしくは昨日の記事をごらんください。

今日は、contextlib.ContextDecoratorを学習します。

ContestDecorator

ContextDecoratorのつづりをみてピンと来た方もいるかも知れませんが、キャメルケースなので、クラスです。

ContextDecoratorというクラスを継承したクラスを作成して、いろいろと値をもたせておきたいときに使うことができます。

昨日と同じ出力結果が得られるコードをContextDecoratorを使って書いてみましょう。

import contextlib

class tag(contextlib.ContextDecorator):
    def __init__(self, name):
        self.name = name
        self.start_tag = '<:{}>'.format(name)
        self.end_tag = '</{}>'.format(name)

    def __enter__(self):
        print(self.start_tag)

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(self.end_tag)

with tag('p'):
    print('text')

# @contextlib.contextmanager
# def tag(name):
#     print('<:{}>'.format(name))
#     yield
#     print('</{}>'.format(name))
#
# with tag('p'):
#     print('text')

18行目から25行目は参考として、昨日のコードを記述しています。

3行目から13行目で、contextmanagerをデコレーターにした部分をContextDecoratorを継承したtagクラスに変更しています。

4行目の__init__(self, name)で引数のnameself.nameにして、start_tagend_tagに引数のnameを入れて、前後のタグを渡しています。

9行目の__enter__(self)start_tagを出力して、12行目の__exit__(self, exc_type, exc_val, exc_tb)end_tagを出力しています。

引数のexc_typeexc_valexc_tbは、エクセプションが起こったときに引数を受け取りますので、デフォルトでNoneが指定されています。

出力結果

<p>
text
</p>

出力結果は昨日と同じになります。

例外を出力してみる

クラスの処理を終了する際の__exit__には、エクセプションの値を受け取るためのexc_typeexc_valexc_tbが用意されているので、print出力してエクセプションで何を受け取っているのか確認してみます。

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        print(self.end_tag)

with tag('p'):
    raise Exception('Error')
    print('text')

出力結果

<p>
<class 'Exception'>
Error
<traceback object at 0x7fe08c09deb0>
</p>
Traceback (most recent call last):
  File "/Users/bigmacpro/PycharmProjects/libproject/lesson.py", line 19, in <module>
    raise Exception('Error')
Exception: Error

19行目のraiseでエクセプションが起こるので、順番にexc_typeは、classで、exc_valは、Errorで、exc_tbは、traceback objectが出力されています。

最後にraise Exception('Error')でプログラムが終了しています。

エクセプションを受け取って、なにか処理をさせることができるのもContextDecoratorの特徴ですね。

まとめ

ちょっと複雑過ぎて、理解するのが難しいのですが、今後の実践で便利に使えるということなので、実際に使う場面になってから詳しく理解したほうがいいかもしれません。

デコレーターやwithステートメント同様に、ContextDecorator関数の実行前後になにかの処理をさせることができて、同じ結果を出力することができましたが、コードは全く違いました。

同じアプリを作るにしても、プログラムを書く人によってコードは全く変わってきますが、基本は、きれいで誰が読んでも読みやすいコードを心がけることが大切です。

よりシンプルで見やすいコードを書けるよう、経験を積み上げましょう。

それでは、明日もGood Python!