Pythonでプログラムを書いていると,
Python はクロージャに対応していて,
クロージャが一番役に立つ場面がデコレータです。次の例では,
リスト1
# coding: utf-8
from __future__ import print_function
import sys
def log(out=sys.stderr):
def decorator(func):
name = func.__name__
def decorated(*args, **kwargs):
### 外側の関数の変数 name, out を利用できる
print("%s start" % name, file=out)
func(*args, **kwargs)
print("%s end" % name, file=out)
return decorated
return decorator
@log(sys.stdout)
def foo(a, b):
print(a + b)
foo(1, 2)
図1 リスト1の実行結果
foo start 3 foo end
Pythonのクロージャには,
リスト2 decorator関数の変更部分
def decorator(func):
name = func.__name__
called = 0 ### カウンタ
def decorated(*args, **kwargs):
called += 1 ### UnboundLocalError: local variable 'called' referenced before assignment
print("%s starts %d times" % (name, called), file=out)
func(*args, **kwargs)
print("%s ends %d times" % (name, called), file=out)
return decorated
図2 変更後のプログラムの実行結果
$ python2.6 test2.py Traceback (most recent call last): File "test2.py", line 22, infoo(1, 2) File "test2.py", line 11, in decorated called += 1 UnboundLocalError: local variable 'called' referenced before assignment
Pythonでは,
このような場合,
リスト3 Python 2.
called = [0] ### リストの最初の要素がカウンタ
def decorated(*args, **kwargs):
called[0] += 1 ### 変数ではなくリストの要素を更新
Python 3.
リスト4 Python 3.
called = 0
def decorated(*args, **kwargs):
nonlocal called ### calledはローカル変数ではない
called += 1 ### O.K.
この制限は,