contextlib contextmanager

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

前回のエギング釣行から少し期間が空いたのですが、イカのストックを増やそうと思ってエギングに行ってきました。

結果は異常なしで、ストックを増やすどころか、エギをロストしてしまいました。

なかなか思いどおりにイカないものですね。

ということで、今日もPython学習を始めましょう。

昨日の復習

昨日は、デコレーターの復習をしました。

インナー関数の実行前後になにかの処理をしてくれるのがデコレーターですが、デコレーターに引数を与えて、前後の処理で引数を活用することもできましたがコードが少し複雑になってしまいましたね。

詳細については昨日の記事をごらんください。

今日は、複雑な処理をわかりやすいコードで記述できるcontextlib.contextmanagerを学習します。

contextmanager

デコレーターで引数をとって関数の実行前後に引数を活用するには、少しコードが複雑になりましたが、contextlib.contextmanagerを使えばかなりコンパクトに記述することができます。

import contextlib

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

@tag('p')
def f(content):
    print(content)

f('text')

# def tag(name):
#     def _tag(f):
#         def wrapper(content):
#             print('<{}>'.format(name))
#             r = f(content)
#             print('</{}>'.format(name))
#             return r
#         return wrapper
#     return _tag

出力結果

<p>
text
</p>

15行目以降に昨日書いたデコレーターのコードを記述していますが、比べてみるとかなりスッキリとしたコードになりました。

デコレーターを二重にしていた部分が外れて、関数の実行部分にyieldが入った形になります。

デコレーターは前後になにか処理をするということなので、contextmanagerが関数tag(name)の前後で処理をして、関数tag()でデコレーターから受け取る引数を扱えるようにしているということです。

withステートメント

さらに、withステートメントを使うと、もっとスッキリとしたコードになります。

import contextlib

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

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

出力結果

<p>
text
</p>

withステートメントは、デコレータと同じような感じで、オブジェクト化して前後になにか処理をさせることができるので、イメージとしては、デコレーターのtag('p')と関数f('text7)の実行を兼ねた処理をしている感じですね。

関数内でwith

withを関数内で使うことで、前後処理の外側で何かをさせることも可能です。

def f():
    print('something')
    with tag('p'):
        print('text')

f()

出力結果

something
<p>
text
</p>

最初のcontext.contextmanagerのコードは同じで、9行目からを変更しています。

関数内でwithを使うことで、他の処理と組み合わせて使うこともできます。

今回は、somethingを出力してみました。

withのネスト

withステートメントをネストさせて、入れ子にすることも可能です。

with tag('p'):
    print('textp')
    with tag('a'):
        print('texta')

出力結果

<p>
textp
<a>
texta
</a>
</p>

上記のコードはネストしていますが、普通に並べて使うことも可能です。

with tag('p'):
    print('textp')
with tag('a'):
    print('texta')

出力結果

<p>
textp
</p>
<a>
texta
</a>

今回の使い方でも紹介していますが、引数を受け取って関数の前後で処理ができるcontext.contextmanagerは、HTMLなどのコードを処理するにはピッタリですね。

まとめ

これまでに学習した酒井さんの講座では、対話型アプリのJarvisを書いたレクチャー以外は、実践的な内容のコードではなく、使い方の紹介がメインとなっています。

Udemy講師 酒井さん プログラミング Python

Pythonの入門から応用編まで網羅しようとするとやはり具体的な使い方まで入れ込むのはかなり難しいんだと思います。

たとえば一つのアプリなりを作ろうと思えば、それだけで一つの講座になりますし、その講座を学習する前提条件として、現在学習中の酒井さんの講座でやっている知識がある程度必要になります。

なので、このような使い方ができるということを頭の片隅に残しておいて、実践に取り組んだときにレファレンス的に酒井さんの講座を活用するという使い方も有りなのかなと思うようになってきました。

まだまだ学習しないといけないことはたくさんありますが、気づけば365日Python学習も残り100日に入りました。

いよいよゴールが見えてきた感じがするので、気を抜かずに継続していきたいと思います。

それでは明日もGood Python!