デコレーターの復習

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

何年ぶりかの旅行に行ってきました。

南紀 那智の滝

GOTOキャンペーンを利用して旅行に行けば、かなりお得になるということだったので、ちょっと贅沢していいホテルに泊まったのですが、日常から開放されてかなり満足度の高い時間を過ごすことができました。

地域共通クーポンももらえたので、お金を使わずにお土産を購入することもできました。

実質、旅行代金の65%の支払いで済むうえ、さらに元の旅行代金の15%が地域共通クーポンがもらえるので、実質の負担は旅行代金の50%、つまり半額で旅行できちゃうので、めっちゃお得です。

とはいえ、支払いは旅行代金の65%は必要で、そのうちの15%はお土産代金としてチャージしているようなものだから半額で旅行できるわけではないのであしからず。。。

それでは、今日もPython学習を始めましょう。

昨日の復習

昨日は、便利なPythonライブラリの一つであるIPythonについて学習しました。

AnacondaをインストールするともれなくついてくるJupyter Notebookの元になっているライブラリなので、Jupyterを使う人には必要ないかもしれませんが、対話型シェルの少し便利なインターフェイスという感じでした。

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

今日は、contextlibを学習しますと言いたいところですが、デコレーターに関するライブラリなので、より理解するためにデコレーターを復習しておきましょう。

デコレーター

デコレーターはこちらの記事で学習しました。

4月に学習しているのですでに記憶に残っていないかもしれませんが、学習の途中で何度も出てきているので、関数の前後に処理をさせることができるという理解はできているのかなと思います。

過去の記事では、具体的な使い方として、HTMLのタグを前後に挟むということを考えましたが、今回の復習もタグで処理をサンドイッチしてみます。

@を使わないでデコレーター

まずは、簡単なデコレーターのコードを書いてみましょう

def tag(f):
    def wrapper():
        print('<p>')
        r = f()
        print('</p>')
        return r
    return wrapper

def f():
    print('text')

f = tag(f)

f()

出力結果

<p>
text
</p>

このコードの12行目を削除して、9行目に@tagとして、デコレーターで使うことができました。

もちろん出力結果は同じ結果になりました。

処理の流れをみてみましょう。

14行目のf()が実行されると、12行目で1行目のtag(f)が呼び出されてwrapperで包まれた関数f()を実行したオブジェクトrが返ってきて、9行目の関数f()rにはtextが入っているので、最終的にタグ(<p></p>)で包まれたtextが返されるというわけです。

実行時に引数を入れる

次に、実行時に関数f()に引数を入れて出力するコードを書いてみます。

def tag(f):
    def wrapper(content):
        print('<p>')
        r = f(content)
        print('</p>')
        return r
    return wrapper

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

f('text')

出力結果

<p>
text
</p>

このコードでも同じ出力結果が得られます。

デコレーターに引数を渡す

これまでデコレーターに引数を付けたことはありませんでしたが、今回デコレーターに引数をわたしてみます。

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

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

f('text')

デコレーターは、ほんとに頭がこんがらがってきますよね

fの引数にcontentを入れているのはそのままですが、1行目でデコレーターの引数nameを指定して、11行目でそのnameに文字列のpを入れています。

2行目のデコレーターは_tag(f)として返り値を_tagにしています。

3行目から8行目のwrapperでは、デコレーターtagの引数nameを活用することができるようになり、'<{}>'.format(name)でタグの名前にnameを入れています。

ほんと自分で中身を確認していても頭がオーバーヒートしちゃうんですよね。

で、このようなややこしいコードをわかりやすくできるのがcontextlibなのですが、頭がこんがらがってるので、明日の学習に持ち越したいと思います。

まとめ

デコレーターってホントややこしいんですよね。

以前学習したときには、関数実行の前後になにかの処理をさせることができるという理解だけだったので、なんとか理解することができたのですが、今回の復習では、さらに複雑になり、デコレーターに引数を入れて処理を行う方法を学習したので、ちょっと難しかったですね。

Pythonの機能は、ほんと多機能でいろいろな使い方があるので、全てをカバーするのは不可能といえますが、取り残されないように、しっかりと頭に詰め込んでいきたいと思います。

ということで、明日もGood Python!