変数のスコープ等
Pythonのローカル変数のスコープはどうなってるのか。
def make_counter(i=0): def succ(): tmp = i i += 1 return tmp return succ counter = make_counter() conter() """ Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in succ UnboundLocalError: local variable 'i' referenced before assignment """
JavaScriptみたいにはいかない。
ではクロージャはないのかというと、
def f(): x = 1 def return_x(): return x return return_x f()() # 1
ある。
外側の変数を参照することはできるけど、書き換えることはできない。
def make_counter(i=0): value = [i] def succ(): tmp = value[0] value[0] += 1 return tmp return succ counter = make_counter() counter() # 0
このように、変数自体を書き換えないようにすると大丈夫。
少し考えて、
def f(): a = range(10) def g(): a = a a = a[:-1] return a[-1] return g f()() """ Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in g UnboundLocalError: local variable 'a' referenced before assignment """
a = aとしたのだけど、うまくいかない。
ローカル変数のaに外側のaが代入されると思ったのだけど、外側の変数に代入しようとしてると解釈されるようだ。
ということで、クロージャで変数に再代入したくなったら、リストやハッシュを使ったり、関数のプロパティに値を持たせたりするといい。
もしくは、callableなクラスを作って、
class Counter(): def __init__(self, i=0): self.i = 0 def __call__(self): tmp = self.i self.i += 1 return tmp counter = Counter() counter() # 0 counter() # 1
ともできる。