Pythonの関数とグローバル変数

February 26, 2011
タイトルのまんまの記事ですが、NukeなどでPythonを書くときの注意メモ。Pythonのグローバル変数は関数内からでも簡単にアクセスできちゃうのですが、ちょっといい加減なコード書くとすぐに変数のスコープのせいで意図しない結果になったりするので注意。

まずすごく簡単なコードを例にあげてみます。

thisval = 1
def getval():
      onval = thisval
      print onval
getval()

Nukeでこれを実行すると、当たり前ですが「# Result: 1」を返します。

thisval = 1
def getval():
      thisval = 100
      retuen thisval
print getval() + thisval

これの結果は「# Result: 101」。関数内でのthisvalはローカルスコープなので、そこで代入してもグローバル変数のthisvalには影響しません。これを以下のように変更するとエラーが出ます。

thisval = 1
def getval():
      onval = thisval
      thisval = 100
      return thisval
print getval() + thisval

# Result: Traceback (most recent call last):
File "<string>", line 6, in <module>
File "<string>", line 3, in getval
UnboundLocalError: local variable 'thisval' referenced before assignment

これは関数内で「thisval = 100」の前に「onval」がグローバルのthisvalを参照しているために、その後の「thisval」代入時に、関数内でローカル変数の宣言を使用として失敗してます。以下のようにglobal変数として関数内で宣言してやれば関数内からグローバル変数に代入できます。

thisval = 1
def getval():
      global thisval
      onval = thisval
      thisval = 100
      return thisval
print getval() + thisval

# Result: 200

最後のprintで参照する順を逆にすると、関数内で代入されるタイミングも変わります。当たり前ですが...

thisval = 1
def getval():
      global thisval
      onval = thisval
      thisval = 100
      return thisval
print thisval + getval()

#Result: 101

いかにもトラブルの原因になりそうですよね...ええ、なるんですよ、実際。
というわけでグローバル変数を直接関数内から参照したり代入したりするのは極力避けたいところです。グローバル変数はread onlyくらいに考えておくか、何か接頭文字を付けるとかして管理しておくくらいの方がいいかと思ってます。

最初の例にもどって見ると、このコードはグローバル変数に関数内からアクセスするのではなく引数として渡してやるのがトラブル回避の意味では正解かと...

thisval = 1
def getval(val):
      onval = val
      print onval
getval(thisval)

これはPythonに限ったことではなくてAEのJavaScriptなんかでも同じことが言えるんだけど、Pythonの場合は「ここで宣言した」っていうのが文字から見えて来ないだけに注意が必要かなと思います。




エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス
売り上げランキング: 18775