関数内のネストした名前空間

関数内のネストした名前空間で、外側の関数の変数の参照は可能だが、再結合は不可能。可変オブジェクトなら内容を書き換えるのに参照のみで再結合の必要ないので、内容を変更可能。しかし、intみたいに不変オブジェクトは参照は可能だが、再結合はできないので値を変更できない。

def foo(a):
  b = 2
  c = [3]
  def bar(d):
    print a, b, c, d  # 参照は可能
    a += 1  # UnboundLocalErrorとなり変更不可能
    b += 2  # UnboundLocalErrorとなり変更不可能
    c[0] = 5  # 変更可能
  bar(4)

これを回避するには、もちろんNFOアプローチを使えばよい。

def foo(a):
  def f():pass  # NFO
  f.a = a
  f.b = 2
  f.c = [3]
  def bar(d):
    print f.a, f.b, f.c
    f.a += 1  # OK!
    f.b += 1  # OK!
    f.c[0] = 5
  bar(4)

再結合する必要がなければ NFO を使用する必要はないが、再結合する必要があれば NFO を使う方が良い。参照は可能なので、以下のように一度ローカル変数に代入してあげれば回避可能だが、NFOアプローチとどっちがいいのだろう?難しいところである。

def foo():
  a = 1
  def bar():
    b = a
    b += 1  # OK!
    print b
  bar()

追記:
よく考えてみたらstatic変数的な使い方をする場合は、最後の回避方法では実現不可能だった。NFO を使うか、可変オブジェクトを使うしかない。Pythonにstatic変数がないという主張があったら、NFO(もしくは可変オブジェクト) とクロージャを組み合わせれば実現可能だよと教えてあげることができる。

また、8/1の2.の関数の呼び出し側に関数内部の情報を渡せるというのは、もちろん、classを使用すれば同様な機能が可能だが、NFO の方が手軽だと言える。