はじめに
githubでpipのリポジトリを見ていたら、面白いデコレータの使い方をしていたので共有します。
TL;DR
- デコレータは関数を修飾するためだけでなく、関数を実行するために用いることもできる。
- 本題
importした時のグローバル変数の値
まず、以下のようなパッケージをimportした時にグローバル変数がどのような値をとるのでしょうか。関数spam
もegg
もグローバル変数global_variable
を更新するためだけの関数です。
GLOBAL_VARIABLE = 0
def spam():
global_variable = 1
globals().update(locals())
def egg():
global global_variable
global_variable = 2
import mypackage
if __name__ == "__main__":
print(mypackage.global_variable)
関数を定義しているだけで実行していないので、global_variable
の値は宣言時から変化していません。実行結果はもちろん以下のようになります。
$python3 main.py
0
では、mypackage/__init__.py
の末尾で関数を呼び出してみるとどうなるでしょうか。
GLOBAL_VARIABLE = 0
def spam():
global_variable = 1
globals().update(locals())
def egg():
global global_variable
global_variable = 2
spam()
egg()
この場合、関数が呼ばれてglobal_variable
の値がspam
内でもegg
内でも上書きされるので、実行結果は以下のようになります。
$python3 main.py
2
しかし、この方法だと、実行したい関数の数が増えるとその分だけ呼び出すためのコードを書かねばならず、面倒ですね。そこで、デコレータの出番です。
デコレータの内部で関数を実行する
ここからが本題です。さっそくコードを見ていきましょう。以下のようなcall_aside
関数を考えましょう。
global_variable = 0
def call_aside(f, *args, **kwargs):
f(*args, **kwargs)
return f
@call_aside
def spam():
global_variable = 1
globals().update(locals())
@call_aside
def egg():
global global_variable
global_variable = 2
import mypackage
if __name__ == "__main__":
print(mypackage.global_variable)
なんと実行結果は、
$python3 main.py
2
のようになります!最初、こうなる理由が分からなくて、結構悩みました。しかし、仕組みは分かると単純です。call_aside
関数に注目すると、
def call_aside(f, *args, **kwargs):
f(*args, **kwargs) # <- execute f
return f
というように、内部で関数を呼び出しています。したがって、call_aside
で修飾しただけで、mypackageがimportされた時点でspam
とegg
が実行さるので、上のようにglobal_variable
の値が変化したというわけです。これなら、importされた時点で実行したい関数の数が増えても装飾するだけで済みますね
補足
Pythonのデコレータについて調べると、**「ある関数の前後に処理を追加する」「前後に処理を追加した関数を返す」**ためにデコレータを用います、とデコレータを解説しているものが多いと思います。
def decorator(f):
def wrapper(*args, **kwargs):
print("前処理")
f(*args, **kwargs)
print("後処理")
return wrapper
このような使い方だと、wrapper
関数は定義されただけで実行されていません。このデコレータで関数を装飾しても、装飾された関数を呼び出すまでは、グローバル変数の値を更新する等の影響は及ぼしません。
参考
pypa/pip/blob/master/src/pip/_vendor/pkg_resources/__init__.py
最後に
ある程度Pythonを読み書きできるようになったら、CPython
やPypa
など公式のgithubを眺めてみてください。そこには、ツヨツヨの方達のコードが無限にあるので、新たな発見があったり、参考になるコードがあったりすると思います。