【Python】デコレーターで彩るデコレーションケーキ

2020.11.06

2021.08.12

Fabeee社員ブログ


ごきげんよう。先々月Fabeeeに入社した、りんです。
ハロウィンも終わった今日この頃、みなさまいかがお過ごしでしょうか。
肌寒くなるにつれ何となくもの悲しくなってくる、アースカラーのこの季節・・・
を感じながらも頭の中はクリスマスでいっぱいなのではないでしょうか。
クリスマスのことを思うと夜しか眠れないのではないでしょうか。
そう!
もういくつ寝るとクリスマスなんですね!!
ということで今回はクリスマスを先取りしてみましょう。
Python歴二ヶ月の筆者が最近学んだPythonの機能デコレーターをプレイバックしつつ
クリスマスに欠かせないケーキをデコレーションしていこうと思います。
Let’s kick it off!
用意するもの
・板チョコ (食べる)
・クリスマスソング (かける)
・Pythonが実行できる環境

 

①まず関数をおさらい

将を射んと欲すればまず馬。デコレーターでデコレーションするための前提知識をおさえます。
Pythonには関数があります。
こういうアレですね。

def say_hello():
    print('Hello!')

これは関数そのもの

say_hello
# →function say_hello at 0x1095c4680

これは関数を実行することを表します。カッコが大事。

say_hello()
# →Hello!

②関数in関数

Pythonでは関数の中に関数を書くことができます。Wow.
先ほどのsay_hello関数を作って返す関数です。

def get_func():
    def say_hello():
        print('Hello!')
    return say_hello
get_func
# →function get_func at 0x10d58e680

関数が返ってきますね。
勘の良い方はお気づきでしょう、そうです。

get_func
# →function get_func at 0x10d58e680
# get_func関数そのもの
get_func()
# →function get_func..say_hello at 0x10d58e710
# say_hello関数
get_func()()
# →Hello!
# say_hello関数の実行

となります。
 

③関数を引数に

Pythonでは関数を引数にとることもできます。
筆者は主にJavaをやってきたのでこの辺りあまり馴染みがなく「ほえーー」って感じですが、できます。そう、パイソーンならね。
※Python以外にもこれできる言語いっぱいあります

def get_func(func):
    return func
def say_hello():
    print('Hello!')
get_func(say_hello)()
# →Hello!

受け取った関数をそのままreturnする関数get_funcに、say_helloを渡しています。
カッコが並んでいてカッコいいわかりにくいですが、変数に入れると少し考えやすくなりますかね。

say_hello_func = get_func(say_hello)
say_hello_func()
# →Hello!

ちなみにこのように関数を返すことができる、関数を引数にとれる関数を高階関数といいます。
 

④デコレーター

やっとデコレーターの話です。
「関数というものがある・関数in関数ができる・関数を引数にとれる」をふまえるとデコレーターが作れます。
さて、ここまでふわっときましたがそもそもデコレーターって何だってばよ?
デコレーターとは、関数を修飾する関数です。
修飾される側には@をつけるので、ぱっと見はJavaのアノテーションのようにも見えますが動きは異なります。
Seeing is Believing, みてみましょう。

def print_awesome(func):
    def inner_func():
        print('★★★★')
        func()  # 引数で受け取った関数を実行
    return inner_func
def say_hello():
    print('Hello!')
print_awesome(say_hello)()
# →★★★★
# →Hello!

say_hello関数は相変わらずHelloと出力するだけですね。
print_awesome関数は、「★を出力してから、引数で受け取った関数を実行する」というinner_func関数を返します。これがデコレーターの正体です。
このprint_awesome関数をデコレーターとして、@をつけて呼んでやります。

@print_awesome
def say_hello():
    print('Hello!')
say_hello()
# →★★★★
# →Hello!

Excellent !
これでデコレーターマスターになれましたね!
 

⑤ケーキをデコレーション

デコレーターの「関数を修飾できる」を応用してケーキをデコレーションしてみましょう。

# ローソクに火をつけるデコレーター
def light_candles(func):
    def inner_func():
        print('     ★★★★★')
        func()
    return inner_func
# かわいいお皿にのせるデコレーター
def serve_on_plate(func):
    def inner_func():
        func()
        print('-*-*-*-*-*-*-*-')
    return inner_func

まずはデコレーションなしのケーキ

def get_cake():
    print('  ╭━━┻┻┻┻┻━━╮')
    print('  ┃╱╲╱╲╱╲╱╲╱┃')
    print(' ╭┻━━━━━━━━━┻╮')
    print(' ┃╲╱╲╱╲╱╲╱╲╱╲┃')
    print(' ┗━━━━━━━━━━━┛')
get_cake()
# →  ╭━━┻┻┻┻┻━━╮
# →  ┃╱╲╱╲╱╲╱╲╱┃
# → ╭┻━━━━━━━━━┻╮
# → ┃╲╱╲╱╲╱╲╱╲╱╲┃
# → ┗━━━━━━━━━━━┛

ちょっとさみしいですね。
先程作ったデコレーターでデコレートしましょう!
デコレーターは複数つけられます。

@light_candles
@serve_on_plate
def get_cake():
    print('  ╭━━┻┻┻┻┻━━╮')
    print('  ┃╱╲╱╲╱╲╱╲╱┃')
    print(' ╭┻━━━━━━━━━┻╮')
    print(' ┃╲╱╲╱╲╱╲╱╲╱╲┃')
    print(' ┗━━━━━━━━━━━┛')
get_cake()
# →     ★★★★★
# →  ╭━━┻┻┻┻┻━━╮
# →  ┃╱╲╱╲╱╲╱╲╱┃
# → ╭┻━━━━━━━━━┻╮
# → ┃╲╱╲╱╲╱╲╱╲╱╲┃
# → ┗━━━━━━━━━━━┛
# → -*-*-*-*-*-*-*-

花丸かわいくなりました!拍手!!
 

⑥まとめ

Pythonの基礎を確認つつ、デコレーターを作ってみました。
ケーキはかわいくなったけど、ぶっちゃけコレいつどこで使うんだ・・・?とお思いのそこのアナタ。
関数を修飾できる
これさえ覚えていればいろいろと応用がききます。
例えば前後にストップウォッチを仕込めば関数の処理時間を計測できますし、
それをログにも出せますし、
共通の前処理/後処理なんかがあればそういったモノもできます。
自分なりの使いどころを探して、君だけの最強のデコレーターを作ろう!
That’s all for today, ありがとうございました!
 

Fabeee編集部

Fabeee編集部

こちらの記事はFabeee編集部が執筆しております。