プログラム実行時に何かの名前(変数名や関数名など)を発見すると、Pythonはそれを解決するために、3つ(またはそれ以上)のスコープを「ローカルスコープ→グローバルスコープ→ビルトインスコープ」の順にたどっていく。つまり、関数の実行時に名前が見つかると、まずは関数のローカルスコープからその名前を検索する。ローカルスコープにその名前があれば、それ(ローカル変数やパラメーターなど)の値を利用する。見つからなければ、グローバルスコープ→ビルトインスコープという順番で、その名前を検索していき、見つかったところでその値を利用する。最終的に名前が見つからなければ、先ほどの画像にあった「NameError」を発生させる。
以下では、名前を解決する順序についてコード例と共に見ていこう。
ここでmyfunc関数についてもう一度見てみよう。
def myfunc():
a = 'Python'
print('a:', a)
a = 'Ruby'
myfunc()
print('a:', a)
myfunc関数本体の1行目では、ローカル変数aを定義している。これは既に述べた通り、ローカルスコープを使って定義されている。これに対して、グローバル変数の方の変数aはグローバルスコープ(=モジュールのローカルスコープ)を使って定義されている。
何げなく書いているが、Pythonの大きな特徴の一つに、「変数に代入をするときには、通常、ローカルスコープが使われる」ことが挙げられる。「ローカル変数」の説明では「関数内での変数への代入はローカル変数の定義や、ローカル変数(やパラメーター)の値の変更と解釈する」と述べたが、これは同じことを別の言い方で表したものだ。
myfunc関数の中にある「a = 'Python'」という代入文は、この規則に従って、ローカルスコープに変数aを定義しているということだ(関数内でグローバル変数を(その値を変更することを目的に)使用することを明示的に宣言することも可能だ。これについては後述する)。
関数本体の2行目にあるprint関数呼び出しで「a」という名前が見つかると、まずはローカルスコープを使って名前を解決しようとする。この場合は、その名前がローカルスコープにあるので、この時点で「a」という名前が解決され、ローカル変数aの値が使われて、print関数が呼び出される。
ここでmyfunc関数の定義を次のように変更してみよう。
def myfunc():
print('b:', b)
今度はローカル変数を定義せず、「print('b:', b)」行では変数bの値を画面に表示するようにしている(変数名を「a」から「b」に変更したのは、説明の都合からだ)。これを呼び出すコードも追加して、以下を実行するとどうなるだろう。
def myfunc():
print('b:', b)
myfunc()
もちろん、どこでも変数bを定義していないので、これは次のようにエラーとなる。
では、先ほどと同様にmyfunc関数を呼び出す前にモジュールレベルでグローバル変数bを定義してみよう。
b = 'Python'
myfunc()
すると、今度はエラーにならずに文字列'Python'が表示された。
ここではどのような順序で名前が解決されているのだろう。最初はローカルスコープを見るので、次のようになる。
1. ローカルスコープ→ローカル変数bはない→×
ローカルスコープで名前が見つからなければ、次にグローバルスコープから「b」という名前が探される。
2. グローバルスコープ→グローバル変数bがある→解決終了
このようにグローバルスコープ(やビルトインスコープ)にある変数でも、それより内側のスコープから、その値を参照する(知ること)はできる。ただし、その値は変更できない。先ほども「関数内での変数への代入はローカル変数の定義や、ローカル変数(やパラメーター)の値の変更と解釈する」と述べたが、関数内部でグローバル変数と同名の変数へ代入しようとするとローカルスコープに新しくローカル変数が作られる。
よって、通常はあくまでもグローバル変数の値は「参照」できるが「変更」できないということだ。
最後にローカルスコープにもグローバルスコープにもない名前の場合が残ったが、これは既に見ている。そう。print関数だ。これは組み込み関数で、その名前はビルトインスコープに存在する。これまであまり気にせず、print関数をはじめとするPythonの組み込み関数を使ってきたが、それらを呼び出すときには、この経路で名前が解決されていたということだ。
既に述べたように、関数内で変数に代入すると、通常はローカル変数が定義される。関数内ではグローバル変数の値を参照はできるが、変更はできない。だが、グローバル変数の値を関数内で変更したいときもある。そのようなときにはglobal文を使って、それがローカル変数ではなくて、グローバル変数であると「宣言」できる。例えば、myfunc関数を次のようにしてみよう。
def myfunc():
global a # 「a」はグローバル変数aであると宣言
a = 'Python' # グローバル変数aの値を変更
print('a:', a)
a = 'Ruby'
myfunc()
print(a)
変数をグローバル宣言するには「global」に続けて、その変数の名前を列挙する(複数あるときにはカンマで区切って並べる)。この例では、変数aが関数にローカルな変数ではなく、グローバル変数aであることを宣言している。よって、「a = 'Python'」という代入文はグローバル変数への代入となる。ほんとうかどうか、実行してみよう。
今度はmyfunc関数呼び出しで「a: Python」と表示された後に、モジュールレベルでのprint関数呼び出しでも「Python」と表示された。これにより、myfunc関数内で使用している変数aがグローバル変数であることが分かった。なお、ローカルスコープ内で、それよりも外側にあるスコープの変数を(値を変更する目的で)使用することを宣言するには、global文に加えてnonlocal文も使える。これについては次回取り上げる。
関数を呼び出す前後でグローバル変数の値が突然変わるのは、あまり想定されていることではない。本来なら関数を呼び出して、その戻り値をグローバル変数に代入する形で変更するべきであり、そうすればそうした事態は避けられる。通常、関数内ではグローバル変数の値を変更できないのもそうした理由からだ。
だが、何らかの事情で関数内でグローバル変数の値を変更する必要があることもある。global文は「この関数ではグローバル変数の値を変更するよ」というプログラマーの意図を示すためのものであり、関数のコードを読むことで、他のプログラマーにもそのことが伝わるようになっている。
Copyright© Digital Advantage Corp. All Rights Reserved.