読者です 読者をやめる 読者になる 読者になる

素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

変数のスコープ等

python

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

ともできる。