静かなる名辞

pythonとプログラミングのこと



pythonのスコープは静的に決まる。だから・・・

概要

 少し疑問に思うことがあったので、書き留めておきます。

 目次

前提

 まず以下のようなコードについて考えます。

>>> def f():
...     print(a)
... 
>>> a = "hoge"
>>> f()
hoge

 ここでf()の中のprint()でaを参照しています。aはローカルスコープで定義されていないため、外のスコープ(この場合はグローバルスコープ)にあるのだろうとpythonインタプリタは判断します。そのため、f()を呼ぶとグローバルスコープで定義したaがprint(a)で出てきます。

 ここまでは特に疑問はないと思います。次にこれについて考えます(上から続けて実行します)。

>>> def f():
...     print(a)
...     a = "fuga"
...     print(a)
... 
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment

 f()の中でaに対する代入を行うと、aはローカル変数とみなされます。位置は関係なく、スコープは定義時に静的に確定します。なので、UnboundLocalErrorというエラーが発生します。

 これは有名な話で、公式ドキュメントのFAQにも載っています。pythonプログラマなら知っていないといけないことです。

プログラミング FAQ — Python 3.6.5 ドキュメント

 ここまでが前提です。

確認したかったことと結果

 こんな関数定義ではどうなるのか。

>>> def f():
...     print(a)
...     if False:
...         a = "fuga"
...     print(a)
... 

 原則どおりならスコープは静的に確定しますが、なんとなく違う結果になるという期待も抱かせます。

 結果。

>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
UnboundLocalError: local variable 'a' referenced before assignment

 変わらないのだった。

まとめ

 「とにかく関数の中で代入されていればローカルスコープ」という原則が何よりも優先される、ということを再確認できました。