Python: decoratorの簡単な練習2

Python

前回のエントリ

Python decoratorの簡単な練習1

の続き。

前回は下記sample1.pyから別の関数forloop()にもデコレーターを反映させたsample1fl.pyを作って、デコレーターは関数の一つ一つの出力をデコレートするのでは無く、全体をデコレートする事が分かった。

def deco(func): # decorator関数の宣言、funcにはfunction printscript()が引数として与えられる
    def wrapper(*args, **kwargs): # デコレーターの中身、*args, **kwargsはデコレートする関数の引数。幾つあっても大丈夫な書き方。
        print('*'*len("Hello world"))
        func(*args, **kwargs) # 引数として渡された関数(printscript)を実行
        print('*'*len("Hello world"))
    return wrapper

@deco
def printscript():
    print("Hello world")

printscript()

今回はforloop()関数は忘れて元のprintscript()を使用して、deco()を変更したdeco2()によるsample2.pyを使用して続ける。

def deco2(func):
    import os
    def wrapper(*args,**kwargs):
        res = '*'*(len("Hello world") + 3) + os.linesep
        res += '*' + func(*args,**kwargs) + '!' + '*' + os.linesep
        res += '*'*(len("Hello world") + 3)
        return res
    return wrapper

@deco2
def printscript():
    return "Hello world"

print(printscript())

sample2.pyでは5行目、デコレーターの”func(args,*kwargs)”で引数として渡された関数printscript()の戻り値を加工している点が大きな違い。
ここで加工する為に、関数printscript()ではsample1.pyの時の’print(“Hello world”)’ではなく、’return “Hello world”‘としている。
returnで渡す = 加工できる形で戻している。

他には、デコレーターの中で変数”res”を設定し、そこに

アスタリスク(asterisk)”*”を元のHello worldの字数 + エクスクラメーションマーク(exclamation mark)”!”の1つ +前後の2つ
を最初と最後(4行目、6行目)に入れて、

5行目ではprintscript()の戻り値の後ろにexclamation markを追加した上で、この前後にasteriskを1つづつ追加、

4行目と5行目には末尾にos.linesepで改行を入れた。

この出来上がった”res”を関数wrapperからの戻り値とし、
その戻り値を受けたデコレーターの関数deco2からの戻り値wrapperとしています。
このwrapperからの戻り値が最終的にdeco2でレコレートされた関数printscript()の出力になる。

このsample2.pyのを実行した時の出力は下記。
見た目的には些細な違いだが、
asteriskが全体を囲む様になっている事、Hello worldにexclamation markが付いている事が分かる。

% python3 sample2.py
**************
*Hello world!*
**************

先程のsample2.pyでは、出力する文字を”Hello world”に決め打ちしていたので、自由に好きな文字で同様の事をしてみたい。

と言う事で加工した下記コードをsample3.pyとして保存する。
実行時に単語、文を続けて書けば、その単語、文をデコレートして出力するコード。

import sys
if len(sys.argv) == 2:
    wd = sys.argv[1]
else:
    wd = ""
    for i in range(1, len(sys.argv)):
        if i == len(sys.argv) - 1:
            wd += sys.argv[i]
        else:
            wd += sys.argv[i] + " "

def deco2(func):
    import os
    def wrapper(*args,**kwargs):
        res = '*'*(len(wd) + 3) + os.linesep
        res += '*' + func(*args,**kwargs) + '!' + '*' + os.linesep
        res += '*'*(len(wd) + 3)
        return res
    return wrapper

@deco2
def printscript():
    return(wd)

print(printscript())

1行目でモジュールsysをインポート、sys.argvでターミナルにおける実行コマンドの後に続けた文字列を取ってきて、その末尾にexclamation markをつけ、全体をasteriskで囲う。

sys.argvはリスト。
このリスト中身はsys.argv[0]はファイル名、sys.argv[1]以降はファイル名に続けて入力した文から来た「単語」が入る。「単語」はスペースで区切られた一塊を一つの「単語」として扱う。

「単語」を認識し、打った通りに変数wdに追加する為、if else文を使用。
最初から途中の「単語」は「単語」+ スペースでwdに追加、最後は「単語」のみの追加。
これで入力した通りの文字列となる。

デコレーターでこの文字列にexclamation markをつけ、全体をasteriskで囲う加工をし、出力する。

出力結果は下記の通り。
因みに、sys.argvの後に続ける文字列のPython’sの箇所に、アポストロフィー(apostrophe)”‘”が含まれているので、入力時にはエスケープのためのバックスラッシュ(backslash)”\”を”‘”の前に置いた”Python\’s”としている。

% python3 sample3.py Hello Python\'s World
***********************
*Hello Python's World!*
***********************

これでどんな文字列を続けて書いても、exclamation markをつけ、全体をasteriskで囲う加工がされる様になった。

Decoratorから始まり脱線で終わった気がするけれど、前よりDecoratorが分かった気がするので良しとする。

11/10追記:pythonファイルの実行と出力結果のソースコードについて、Visual Studioでの出力結果を記載していた為にターミナル表記と異なっていた点を修正しました。

コメント

タイトルとURLをコピーしました